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

reorder.c

Go to the documentation of this file.
00001 /*************************************************************************
00002  *
00003  *  file:  reorder.c
00004  *
00005  * =======================================================================
00006  *  
00007  *  BUGBUG comments here
00008  *  
00009  *  
00010  * =======================================================================
00011  *
00012  * Copyright 1995-2003 Carnegie Mellon University,
00013  *                                                                               University of Michigan,
00014  *                                                                               University of Southern California/Information
00015  *                                                                               Sciences Institute. All rights reserved.
00016  *                                                                              
00017  * Redistribution and use in source and binary forms, with or without
00018  * modification, are permitted provided that the following conditions are met:
00019  *
00020  * 1.   Redistributions of source code must retain the above copyright notice,
00021  *              this list of conditions and the following disclaimer. 
00022  * 2.   Redistributions in binary form must reproduce the above copyright notice,
00023  *              this list of conditions and the following disclaimer in the documentation
00024  *              and/or other materials provided with the distribution. 
00025  *
00026  * THIS SOFTWARE IS PROVIDED BY THE SOAR CONSORTIUM ``AS IS'' AND ANY EXPRESS OR
00027  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
00028  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
00029  * EVENT SHALL THE SOAR CONSORTIUM  OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
00030  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00031  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00032  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00033  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00034  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00035  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00036  * The views and conclusions contained in the software and documentation are
00037  * those of the authors and should not be interpreted as representing official
00038  * policies, either expressed or implied, of Carnegie Mellon University, the
00039  * University of Michigan, the University of Southern California/Information
00040  * Sciences Institute, or the Soar consortium.
00041  * =======================================================================
00042  */
00043 
00044 #include "soarkernel.h"
00045 #include <ctype.h>
00046 
00047 /* *********************************************************************
00048 
00049                              Reordering
00050 
00051 ********************************************************************* */
00052 
00053 /* =====================================================================
00054 
00055                   Name of production being reordered
00056 
00057    In case any errors are encountered during reordering, this variable
00058    holds the name of the production currently being reordered, so it
00059    can be printed with the error message.
00060 ===================================================================== */
00061 
00062 char *name_of_production_being_reordered;
00063 
00064 #define symbol_is_constant_or_marked_variable(sym,tc) \
00065   ( ((sym)->common.symbol_type!=VARIABLE_SYMBOL_TYPE) || \
00066     ((sym)->var.tc_num == (tc)) )
00067 
00068 /* =====================================================================
00069 
00070                       Reordering for RHS Actions
00071 
00072   Whenever a new identifier is created, we need to know (at creation time)
00073   what level of the goal stack it's at.  If the <newid> appears in the
00074   attribute or value slot of a make, we just give it the same level as
00075   whatever's in the id slot of that make.  But if <newid> appears in the
00076   id slot of a make, we can't tell what level it goes at.  
00077 
00078   To avoid this problem, we reorder the list of RHS actions so that when
00079   the actions are executed (in the resulting order), each <newid> is
00080   encountered in an attribute or value slot *before* it is encountered
00081   in an id slot.
00082   
00083   Furthermore, we make sure all arguments to function calls are bound
00084   before the function call is executed.
00085   
00086   Reorder_action_list() does the reordering.  Its parameter action_list
00087   is reordered in place (destructively modified).  It also requires at entry
00088   that the variables bound on the LHS are marked.  The function returns
00089   TRUE if successful, FALSE if it was unable to produce a legal ordering.
00090 ===================================================================== */
00091 
00092 bool legal_to_execute_action(action * a, tc_number tc);
00093 
00094 bool reorder_action_list(action ** action_list, tc_number lhs_tc)
00095 {
00096     list *new_bound_vars;
00097     action *remaining_actions;
00098     action *first_action, *last_action;
00099     action *a, *prev_a;
00100     bool result_flag;
00101 
00102     new_bound_vars = NIL;
00103     remaining_actions = *action_list;
00104     first_action = NIL;
00105     last_action = NIL;
00106 
00107     while (remaining_actions) {
00108         /* --- scan through remaining_actions, look for one that's legal --- */
00109         prev_a = NIL;
00110         a = remaining_actions;
00111         for (;;) {
00112             if (!a)
00113                 break;          /* looked at all candidates, but none were legal */
00114             if (legal_to_execute_action(a, lhs_tc))
00115                 break;
00116             prev_a = a;
00117             a = a->next;
00118         }
00119         if (!a)
00120             break;
00121         /* --- move action a from remaining_actions to reordered list --- */
00122         if (prev_a)
00123             prev_a->next = a->next;
00124         else
00125             remaining_actions = a->next;
00126         a->next = NIL;
00127         if (last_action)
00128             last_action->next = a;
00129         else
00130             first_action = a;
00131         last_action = a;
00132         /* --- add new variables from a to new_bound_vars --- */
00133         add_all_variables_in_action(a, lhs_tc, &new_bound_vars);
00134     }
00135 
00136     if (remaining_actions) {
00137         /* --- there are remaining_actions but none can be legally added --- */
00138         print("Error: production %s has a bad RHS--\n", name_of_production_being_reordered);
00139         print("       Either it creates structure not connected to anything\n");
00140         print("       else in WM, or it tries to pass an unbound variable as\n");
00141         print("       an argument to a function.\n");
00142         /* --- reconstruct list of all actions --- */
00143         if (last_action)
00144             last_action->next = remaining_actions;
00145         else
00146             first_action = remaining_actions;
00147         result_flag = FALSE;
00148     } else {
00149         result_flag = TRUE;
00150     }
00151 
00152     /* --- unmark variables that we just marked --- */
00153     unmark_variables_and_free_list(new_bound_vars);
00154 
00155     /* --- return final result --- */
00156     *action_list = first_action;
00157     return result_flag;
00158 }
00159 
00160 bool all_variables_in_rhs_value_bound(rhs_value rv, tc_number tc)
00161 {
00162     cons *c;
00163     list *fl;
00164     Symbol *sym;
00165 
00166     if (rhs_value_is_funcall(rv)) {
00167         /* --- function calls --- */
00168         fl = rhs_value_to_funcall_list(rv);
00169         for (c = fl->rest; c != NIL; c = c->rest)
00170             if (!all_variables_in_rhs_value_bound(c->first, tc))
00171                 return FALSE;
00172         return TRUE;
00173     } else {
00174         /* --- ordinary (symbol) rhs values --- */
00175         sym = rhs_value_to_symbol(rv);
00176         if (sym->common.symbol_type == VARIABLE_SYMBOL_TYPE)
00177             return (bool) (sym->var.tc_num == tc);
00178         return TRUE;
00179     }
00180 }
00181 
00182 bool legal_to_execute_action(action * a, tc_number tc)
00183 {
00184     if (a->type == MAKE_ACTION) {
00185         if (!all_variables_in_rhs_value_bound(a->id, tc))
00186             return FALSE;
00187         if (rhs_value_is_funcall(a->attr) && (!all_variables_in_rhs_value_bound(a->attr, tc)))
00188             return FALSE;
00189         if (rhs_value_is_funcall(a->value) && (!all_variables_in_rhs_value_bound(a->value, tc)))
00190             return FALSE;
00191         if (preference_is_binary(a->preference_type) &&
00192             rhs_value_is_funcall(a->referent) && (!all_variables_in_rhs_value_bound(a->referent, tc)))
00193             return FALSE;
00194         return TRUE;
00195     }
00196     /* --- otherwise it's a function call; make sure args are all bound  --- */
00197     return all_variables_in_rhs_value_bound(a->value, tc);
00198 }
00199 
00200 /* =====================================================================
00201 
00202                  Condition Simplification / Restoration
00203 
00204   In order to be able to move tests from one condition to another, we
00205   reorder using the following high-level technique.  (This procedure is
00206   applied separately at each level of nesting.)
00207 
00208     1. Simplify the positive conditions, by stripping off all tests other
00209        than equality.  When this is done, all tests in positive conditions
00210        are either equality tests or conjunctions of equality tests.  All
00211        other tests are saved away for later restoration.
00212     2. Then do the reordering...
00213     3. Then go back and restore all the tests that were previously saved
00214        away.  The restored tests might end up on different conditions
00215        than they started--they're inserted in the first place possible
00216        (i.e., as soon as all the necessary things are bound).
00217 
00218   The two main routines here are simplify_condition_list() and
00219   restore_and_deallocate_saved_tests().
00220        
00221 ===================================================================== */
00222 
00223 typedef struct saved_test_struct {
00224     struct saved_test_struct *next;
00225     Symbol *var;
00226     test the_test;
00227 } saved_test;
00228 
00229 void print_saved_test(saved_test * st)
00230 {
00231     print_with_symbols("  Symbol: %y  Test: ", st->var);
00232     print_string(test_to_string(st->the_test, NULL, 0));
00233 }
00234 
00235 void print_saved_test_list(saved_test * st)
00236 {
00237     while (st) {
00238         print_saved_test(st);
00239         print("\n");
00240         st = st->next;
00241     }
00242 }
00243 
00244 saved_test *simplify_test(test * t, saved_test * old_sts)
00245 {
00246     test new, subtest;
00247     saved_test *saved;
00248     Symbol *var, *sym;
00249     cons *c, *prev_c, *next_c;
00250     complex_test *ct;
00251 
00252     if (test_is_blank_test(*t)) {
00253         sym = generate_new_variable("dummy-");
00254         *t = make_equality_test_without_adding_reference(sym);
00255         return old_sts;
00256     }
00257 
00258     if (test_is_blank_or_equality_test(*t)) {
00259         return old_sts;
00260     }
00261 
00262     ct = complex_test_from_test(*t);
00263 
00264     switch (ct->type) {
00265 
00266     case CONJUNCTIVE_TEST:
00267         /* --- look at subtests for an equality test --- */
00268         sym = NIL;
00269         for (c = ct->data.conjunct_list; c != NIL; c = c->rest) {
00270             subtest = c->first;
00271             if (test_is_blank_or_equality_test(subtest))
00272                 sym = referent_of_equality_test(subtest);
00273         }
00274         /* --- if no equality test was found, generate a variable for it --- */
00275         if (!sym) {
00276             sym = generate_new_variable("dummy-");
00277             new = make_equality_test_without_adding_reference(sym);
00278             allocate_cons(&c);
00279             c->first = new;
00280             c->rest = ct->data.conjunct_list;
00281             ct->data.conjunct_list = c;
00282         }
00283         /* --- scan through, create saved_test for subtests except equality --- */
00284         prev_c = NIL;
00285         c = ct->data.conjunct_list;
00286         while (c) {
00287             next_c = c->rest;
00288             subtest = c->first;
00289             if (!test_is_blank_or_equality_test(subtest)) {
00290                 /* --- create saved_test, splice this cons out of conjunct_list --- */
00291                 allocate_with_pool(&current_agent(saved_test_pool), &saved);
00292                 saved->next = old_sts;
00293                 old_sts = saved;
00294                 saved->var = sym;
00295                 symbol_add_ref(sym);
00296                 saved->the_test = subtest;
00297                 if (prev_c)
00298                     prev_c->rest = next_c;
00299                 else
00300                     ct->data.conjunct_list = next_c;
00301                 free_cons(c);
00302             } else {
00303                 prev_c = c;
00304             }
00305             c = next_c;
00306         }
00307         break;
00308 
00309     default:
00310         /* --- goal/impasse, disjunction, and non-equality relational tests --- */
00311         var = generate_new_variable("dummy-");
00312         new = make_equality_test_without_adding_reference(var);
00313         allocate_with_pool(&current_agent(saved_test_pool), &saved);
00314         saved->next = old_sts;
00315         old_sts = saved;
00316         saved->var = var;
00317         symbol_add_ref(var);
00318         saved->the_test = *t;
00319         *t = new;
00320         break;
00321     }
00322     return old_sts;
00323 }
00324 
00325 saved_test *simplify_condition_list(condition * conds_list)
00326 {
00327     condition *c;
00328     saved_test *sts;
00329 
00330     sts = NIL;
00331     for (c = conds_list; c != NIL; c = c->next) {
00332         if (c->type == POSITIVE_CONDITION) {
00333             sts = simplify_test(&(c->data.tests.id_test), sts);
00334             sts = simplify_test(&(c->data.tests.attr_test), sts);
00335             sts = simplify_test(&(c->data.tests.value_test), sts);
00336         }
00337     }
00338     return sts;
00339 }
00340 
00341 byte reverse_direction_of_relational_test(byte type)
00342 {
00343     switch (type) {
00344     case NOT_EQUAL_TEST:
00345         return NOT_EQUAL_TEST;
00346     case LESS_TEST:
00347         return GREATER_TEST;
00348     case GREATER_TEST:
00349         return LESS_TEST;
00350     case LESS_OR_EQUAL_TEST:
00351         return GREATER_OR_EQUAL_TEST;
00352     case GREATER_OR_EQUAL_TEST:
00353         return LESS_OR_EQUAL_TEST;
00354     case SAME_TYPE_TEST:
00355         return SAME_TYPE_TEST;
00356     default:
00357         {
00358             char msg[MESSAGE_SIZE];
00359             strncpy(msg, "Internal error: arg to reverse_direction_of_relational_test\n", MESSAGE_SIZE);
00360             msg[MESSAGE_SIZE - 1] = 0;
00361             abort_with_fatal_error(msg);
00362         }
00363     }
00364     return 0;                   /* unreachable, but without it, gcc -Wall warns here */
00365 }
00366 
00367 saved_test *restore_saved_tests_to_test(test * t,
00368                                         bool is_id_field, tc_number bound_vars_tc_number, saved_test * tests_to_restore)
00369 {
00370     saved_test *st, *prev_st, *next_st;
00371     bool added_it;
00372     Symbol *referent;
00373     complex_test *ct;
00374 
00375     prev_st = NIL;
00376     st = tests_to_restore;
00377     while (st) {
00378         next_st = st->next;
00379         added_it = FALSE;
00380         ct = complex_test_from_test(st->the_test);
00381         switch (ct->type) {
00382         case GOAL_ID_TEST:
00383         case IMPASSE_ID_TEST:
00384             if (!is_id_field)
00385                 break;          /* goal/impasse tests only go in id fields */
00386             /* ... otherwise fall through to the next case below ... */
00387         case DISJUNCTION_TEST:
00388             if (test_includes_equality_test_for_symbol(*t, st->var)) {
00389                 add_new_test_to_test_if_not_already_there(t, st->the_test);
00390                 added_it = TRUE;
00391             }
00392             break;
00393         default:               /* --- st->test is a relational test other than equality --- */
00394             referent = ct->data.referent;
00395             if (test_includes_equality_test_for_symbol(*t, st->var)) {
00396                 if (symbol_is_constant_or_marked_variable(referent, bound_vars_tc_number) || (st->var == referent)) {
00397                     add_new_test_to_test_if_not_already_there(t, st->the_test);
00398                     added_it = TRUE;
00399                 }
00400             } else if (test_includes_equality_test_for_symbol(*t, referent)) {
00401                 if (symbol_is_constant_or_marked_variable(st->var, bound_vars_tc_number) || (st->var == referent)) {
00402                     ct->type = reverse_direction_of_relational_test(ct->type);
00403                     ct->data.referent = st->var;
00404                     st->var = referent;
00405                     add_new_test_to_test_if_not_already_there(t, st->the_test);
00406                     added_it = TRUE;
00407                 }
00408             }
00409             break;
00410         }                       /* end of switch statement */
00411         if (added_it) {
00412             if (prev_st)
00413                 prev_st->next = next_st;
00414             else
00415                 tests_to_restore = next_st;
00416             symbol_remove_ref(st->var);
00417             free_with_pool(&current_agent(saved_test_pool), st);
00418         } else {
00419             prev_st = st;
00420         }
00421         st = next_st;
00422     }                           /* end of while (st) */
00423     return tests_to_restore;
00424 }
00425 
00426 void restore_and_deallocate_saved_tests(condition * conds_list,
00427                                         /* tc number for vars bound outside */
00428                                         tc_number tc, saved_test * tests_to_restore)
00429 {
00430     condition *cond;
00431     list *new_vars;
00432 
00433     new_vars = NIL;
00434     for (cond = conds_list; cond != NIL; cond = cond->next) {
00435         if (cond->type != POSITIVE_CONDITION)
00436             continue;
00437         tests_to_restore = restore_saved_tests_to_test((&cond->data.tests.id_test), TRUE, tc, tests_to_restore);
00438         add_bound_variables_in_test(cond->data.tests.id_test, tc, &new_vars);
00439         tests_to_restore = restore_saved_tests_to_test((&cond->data.tests.attr_test), FALSE, tc, tests_to_restore);
00440         add_bound_variables_in_test(cond->data.tests.attr_test, tc, &new_vars);
00441         tests_to_restore = restore_saved_tests_to_test((&cond->data.tests.value_test), FALSE, tc, tests_to_restore);
00442         add_bound_variables_in_test(cond->data.tests.value_test, tc, &new_vars);
00443     }
00444     if (tests_to_restore) {
00445         if (current_agent(sysparams)[PRINT_WARNINGS_SYSPARAM]) {
00446             print("\nWarning:  in production %s,\n", name_of_production_being_reordered);
00447             print("      ignoring test(s) whose referent is unbound:\n");
00448             print_saved_test_list(tests_to_restore);
00449         }
00450         /* ought to deallocate the saved tests, but who cares */
00451     }
00452     unmark_variables_and_free_list(new_vars);
00453 }
00454 
00455 /* =====================================================================
00456 
00457            Finding The Variables in a Negated Condition (or NCC)
00458                 That Refer to Variables Bound Outside
00459 
00460   If a variable occurs within a negated condition (or NCC), and that 
00461   same variable is bound by some positive condition outside the negation,
00462   then the reorderer must ensure that the positive (binding) condition 
00463   comes before the negated (testing) condition.  To do this, we put
00464   a list on every NC or NCC of all the variables whose bindings it requires.
00465 
00466   When the reorderer is finished, these lists are removed.
00467   
00468   The main routines here are fill_in_vars_requiring_bindings() and
00469   remove_vars_requiring_bindings().  Each of these recursively traverses
00470   the lhs and does its work at all nesting levels.
00471   Fill_in_vars_requiring_bindings() takes a tc_number parameter which
00472   indicates the variables that are bound outside the given condition list.
00473   (At the top level, this should be *no* variables.)
00474   
00475 ===================================================================== */
00476 
00477 list *collect_vars_tested_by_test_that_are_bound(test t, tc_number tc, list * starting_list)
00478 {
00479     cons *c;
00480     complex_test *ct;
00481     Symbol *referent;
00482 
00483     if (test_is_blank_test(t))
00484         return starting_list;
00485 
00486     if (test_is_blank_or_equality_test(t)) {
00487         referent = referent_of_equality_test(t);
00488         if (referent->common.symbol_type == VARIABLE_SYMBOL_TYPE)
00489             if (referent->var.tc_num == tc)
00490                 starting_list = add_if_not_member(referent, starting_list);
00491         return starting_list;
00492     }
00493 
00494     ct = complex_test_from_test(t);
00495 
00496     switch (ct->type) {
00497     case GOAL_ID_TEST:
00498     case IMPASSE_ID_TEST:
00499     case DISJUNCTION_TEST:
00500         return starting_list;
00501 
00502     case CONJUNCTIVE_TEST:
00503         for (c = ct->data.conjunct_list; c != NIL; c = c->rest)
00504             starting_list = collect_vars_tested_by_test_that_are_bound(c->first, tc, starting_list);
00505         return starting_list;
00506 
00507     default:
00508         /* --- relational tests other than equality --- */
00509         referent = ct->data.referent;
00510         if (referent->common.symbol_type == VARIABLE_SYMBOL_TYPE)
00511             if (referent->var.tc_num == tc)
00512                 starting_list = add_if_not_member(referent, starting_list);
00513         return starting_list;
00514     }
00515 }
00516 
00517 list *collect_vars_tested_by_cond_that_are_bound(condition * cond, tc_number tc, list * starting_list)
00518 {
00519     condition *c;
00520 
00521     if (cond->type == CONJUNCTIVE_NEGATION_CONDITION) {
00522         /* --- conjuctive negations --- */
00523         for (c = cond->data.ncc.top; c != NIL; c = c->next)
00524             starting_list = collect_vars_tested_by_cond_that_are_bound(c, tc, starting_list);
00525     } else {
00526         /* --- positive, negative conditions --- */
00527         starting_list = collect_vars_tested_by_test_that_are_bound(cond->data.tests.id_test, tc, starting_list);
00528         starting_list = collect_vars_tested_by_test_that_are_bound(cond->data.tests.attr_test, tc, starting_list);
00529         starting_list = collect_vars_tested_by_test_that_are_bound(cond->data.tests.value_test, tc, starting_list);
00530     }
00531     return starting_list;
00532 }
00533 
00534 void fill_in_vars_requiring_bindings(condition * cond_list, tc_number tc)
00535 {
00536     list *new_bound_vars;
00537     condition *c;
00538 
00539     /* --- add anything bound in a positive condition at this level --- */
00540     new_bound_vars = NIL;
00541     for (c = cond_list; c != NIL; c = c->next)
00542         if (c->type == POSITIVE_CONDITION)
00543             add_bound_variables_in_condition(c, tc, &new_bound_vars);
00544 
00545     /* --- scan through negated and NC cond's, fill in stuff --- */
00546     for (c = cond_list; c != NIL; c = c->next) {
00547         if (c->type != POSITIVE_CONDITION)
00548             c->reorder.vars_requiring_bindings = collect_vars_tested_by_cond_that_are_bound(c, tc, NIL);
00549         if (c->type == CONJUNCTIVE_NEGATION_CONDITION)
00550             fill_in_vars_requiring_bindings(c->data.ncc.top, tc);
00551     }
00552 
00553     unmark_variables_and_free_list(new_bound_vars);
00554 }
00555 
00556 void remove_vars_requiring_bindings(condition * cond_list)
00557 {
00558     condition *c;
00559 
00560     /* --- scan through negated and NC cond's, remove lists from them --- */
00561     for (c = cond_list; c != NIL; c = c->next) {
00562         if (c->type != POSITIVE_CONDITION)
00563             free_list(c->reorder.vars_requiring_bindings);
00564         if (c->type == CONJUNCTIVE_NEGATION_CONDITION)
00565             remove_vars_requiring_bindings(c->data.ncc.top);
00566     }
00567 }
00568 
00569 /* =====================================================================
00570 
00571              Finding the Root Variables in a Condition List
00572 
00573    This routine finds the root variables in a given condition list.
00574    The caller should setup the current tc to be the set of variables
00575    bound outside the given condition list.  (This should normally be
00576    an empty TC, except when the condition list is the subconditions
00577    of an NCC.)  If the "allow_printing_warnings" flag is TRUE, then
00578    this routine makes sure each root variable is accompanied by a
00579    goal or impasse id test, and prints a warning message if it isn't.
00580 ===================================================================== */
00581 
00582 list *collect_root_variables(condition * cond_list, tc_number tc,       /* for vars bound outside */
00583                              bool allow_printing_warnings)
00584 {
00585 
00586     list *new_vars_from_value_slot;
00587     list *new_vars_from_id_slot;
00588     cons *c;
00589     condition *cond;
00590     bool found_goal_impasse_test;
00591 
00592     /* --- find everthing that's in the value slot of some condition --- */
00593     new_vars_from_value_slot = NIL;
00594     for (cond = cond_list; cond != NIL; cond = cond->next)
00595         if (cond->type == POSITIVE_CONDITION)
00596             add_bound_variables_in_test(cond->data.tests.value_test, tc, &new_vars_from_value_slot);
00597 
00598     /* --- now see what else we can add by throwing in the id slot --- */
00599     new_vars_from_id_slot = NIL;
00600     for (cond = cond_list; cond != NIL; cond = cond->next)
00601         if (cond->type == POSITIVE_CONDITION)
00602             add_bound_variables_in_test(cond->data.tests.id_test, tc, &new_vars_from_id_slot);
00603 
00604     /* --- unmark everything we just marked --- */
00605     unmark_variables_and_free_list(new_vars_from_value_slot);
00606     for (c = new_vars_from_id_slot; c != NIL; c = c->rest)
00607         ((Symbol *) (c->first))->var.tc_num = 0;
00608 
00609     /* --- make sure each root var has some condition with goal/impasse --- */
00610     if (allow_printing_warnings && current_agent(sysparams)[PRINT_WARNINGS_SYSPARAM]) {
00611         for (c = new_vars_from_id_slot; c != NIL; c = c->rest) {
00612             found_goal_impasse_test = FALSE;
00613             for (cond = cond_list; cond != NIL; cond = cond->next) {
00614                 if (cond->type != POSITIVE_CONDITION)
00615                     continue;
00616                 if (test_includes_equality_test_for_symbol(cond->data.tests.id_test, c->first))
00617                     if (test_includes_goal_or_impasse_id_test(cond->data.tests.id_test, TRUE, TRUE)) {
00618                         found_goal_impasse_test = TRUE;
00619                         break;
00620                     }
00621             }
00622             if (!found_goal_impasse_test) {
00623                 print("\nWarning: On the LHS of production %s, identifier ", name_of_production_being_reordered);
00624                 print_with_symbols("%y is not connected to any goal or impasse.\n", (Symbol *) (c->first));
00625             }
00626         }
00627     }
00628 
00629     return new_vars_from_id_slot;
00630 }
00631 
00632 /* =====================================================================
00633 
00634                      Reordering for LHS Conditions
00635 
00636    (Sorry for the poor comments here.  I think the reorderer needs
00637    substantial revisions in order to make Soar reliably scalable, so
00638    most of this code will eventually get thrown out anyway...)
00639 ===================================================================== */
00640 
00641 /* --- estimated k-search branching factors --- */
00642 #define MAX_COST 10000005       /* cost of a disconnected condition */
00643 #define BF_FOR_ACCEPTABLE_PREFS 8       /* cost of (. ^. <var> +) */
00644 #define BF_FOR_VALUES 8         /* cost of (. ^. <var>) */
00645 #define BF_FOR_ATTRIBUTES 8     /* cost of (. ^<var> .) */
00646 
00647 /* -------------------------------------------------------------
00648    Return TRUE iff the given test is covered by the previously
00649    bound variables.  The set of previously bound variables is
00650    given by the current TC, PLUS any variables in the list
00651    "extra_vars."
00652 ------------------------------------------------------------- */
00653 
00654 bool test_covered_by_bound_vars(test t, tc_number tc, list * extra_vars)
00655 {
00656     cons *c;
00657     Symbol *referent;
00658     complex_test *ct;
00659 
00660     if (test_is_blank_test(t))
00661         return FALSE;
00662 
00663     if (test_is_blank_or_equality_test(t)) {
00664         referent = referent_of_equality_test(t);
00665         if (symbol_is_constant_or_marked_variable(referent, tc))
00666             return TRUE;
00667         if (extra_vars)
00668             return member_of_list(referent, extra_vars);
00669         return FALSE;
00670     }
00671 
00672     ct = complex_test_from_test(t);
00673     if (ct->type == CONJUNCTIVE_TEST) {
00674         for (c = ct->data.conjunct_list; c != NIL; c = c->rest)
00675             if (test_covered_by_bound_vars(c->first, tc, extra_vars))
00676                 return TRUE;
00677     }
00678     return FALSE;
00679 }
00680 
00681 /* -------------------------------------------------------------
00682    Returns the user set value of the expected match cost of the
00683    multi-attribute, or 1 if the input symbol isn't in the user
00684    set list.
00685 ------------------------------------------------------------- */
00686 
00687 long get_cost_of_possible_multi_attribute(Symbol * sym)
00688 {
00689     multi_attribute *m = current_agent(multi_attributes);
00690     while (m) {
00691         if (m->symbol == sym)
00692             return m->value;
00693         m = m->next;
00694     }
00695     return 1;
00696 }
00697 
00698 /* -------------------------------------------------------------
00699    Return an estimate of the "cost" of the given condition.
00700    The current TC should be the set of previously bound variables;
00701    "root_vars_not_bound_yet" should be the set of other root
00702    variables.
00703 ------------------------------------------------------------- */
00704 
00705 long cost_of_adding_condition(condition * cond, tc_number tc, list * root_vars_not_bound_yet)
00706 {
00707     cons *c;
00708     long result;
00709 
00710     /* --- handle the common simple case quickly up front --- */
00711     if ((!root_vars_not_bound_yet) &&
00712         (cond->type == POSITIVE_CONDITION) &&
00713         (test_is_blank_or_equality_test(cond->data.tests.id_test)) &&
00714         (test_is_blank_or_equality_test(cond->data.tests.attr_test)) &&
00715         (test_is_blank_or_equality_test(cond->data.tests.value_test)) &&
00716         (!test_is_blank_test(cond->data.tests.id_test)) &&
00717         (!test_is_blank_test(cond->data.tests.attr_test)) && (!test_is_blank_test(cond->data.tests.value_test))) {
00718 
00719         if (!symbol_is_constant_or_marked_variable(referent_of_equality_test(cond->data.tests.id_test), tc))
00720             return MAX_COST;
00721         if (symbol_is_constant_or_marked_variable(referent_of_equality_test(cond->data.tests.attr_test), tc))
00722             result = get_cost_of_possible_multi_attribute(referent_of_equality_test(cond->data.tests.attr_test));
00723         else
00724             result = BF_FOR_ATTRIBUTES;
00725 
00726         if (!symbol_is_constant_or_marked_variable(referent_of_equality_test(cond->data.tests.value_test), tc)) {
00727             if (cond->test_for_acceptable_preference)
00728                 result = result * BF_FOR_ACCEPTABLE_PREFS;
00729             else
00730                 result = result * BF_FOR_VALUES;
00731         }
00732         return result;
00733     }
00734     /* --- end of common simple case --- */
00735     if (cond->type == POSITIVE_CONDITION) {
00736         /* --- for pos cond's, check what's bound, etc. --- */
00737         if (!test_covered_by_bound_vars(cond->data.tests.id_test, tc, root_vars_not_bound_yet))
00738             return MAX_COST;
00739         if (test_covered_by_bound_vars(cond->data.tests.attr_test, tc, root_vars_not_bound_yet))
00740             result = 1;
00741         else
00742             result = BF_FOR_ATTRIBUTES;
00743         if (!test_covered_by_bound_vars(cond->data.tests.value_test, tc, root_vars_not_bound_yet)) {
00744             if (cond->test_for_acceptable_preference)
00745                 result = result * BF_FOR_ACCEPTABLE_PREFS;
00746             else
00747                 result = result * BF_FOR_VALUES;
00748         }
00749         return result;
00750     }
00751     /* --- negated or NC conditions:  just check whether all variables
00752        requiring bindings are actually bound.  If so, return 1, else
00753        return MAX_COST --- */
00754     for (c = cond->reorder.vars_requiring_bindings; c != NIL; c = c->rest) {
00755         if (((Symbol *) (c->first))->var.tc_num != tc)
00756             return MAX_COST;
00757     }
00758     return 1;
00759 }
00760 
00761 /* -------------------------------------------------------------
00762    Return an estimate of the "cost" of the lowest-cost condition
00763    that could be added next, IF the given "chosen" condition is
00764    added first.
00765 ------------------------------------------------------------- */
00766 
00767 long find_lowest_cost_lookahead(condition * candidates,
00768                                 condition * chosen, tc_number tc, list * root_vars_not_bound_yet)
00769 {
00770     condition *c;
00771     long min_cost, cost;
00772     list *new_vars;
00773 
00774     new_vars = NIL;
00775     add_bound_variables_in_condition(chosen, tc, &new_vars);
00776     min_cost = MAX_COST + 1;
00777     for (c = candidates; c != NIL; c = c->next) {
00778         if (c == chosen)
00779             continue;
00780         cost = cost_of_adding_condition(c, tc, root_vars_not_bound_yet);
00781         if (cost < min_cost) {
00782             min_cost = cost;
00783             if (cost <= 1)
00784                 break;
00785         }
00786     }
00787     unmark_variables_and_free_list(new_vars);
00788     return min_cost;
00789 }
00790 
00791 /* -------------------------------------------------------------
00792    Reorder the given list of conditions.  The "top_of_conds" and
00793    "bottom_of_conds" arguments are destructively modified to reflect
00794    the reordered conditions.  The "bound_vars_tc_number"
00795    should reflect the variables bound outside the given condition list.
00796    The "reorder_nccs" flag indicates whether it is necessary to
00797    recursively reorder the subconditions of NCC's.  (For newly
00798    built chunks, this is never necessary.)
00799 ------------------------------------------------------------- */
00800 
00801 void reorder_condition_list(condition ** top_of_conds,
00802                             condition ** bottom_of_conds, list * roots, tc_number tc, bool reorder_nccs);
00803 
00804 void reorder_simplified_conditions(condition ** top_of_conds,
00805                                    condition ** bottom_of_conds,
00806                                    list * roots, tc_number bound_vars_tc_number, bool reorder_nccs)
00807 {
00808     condition *remaining_conds; /* header of dll */
00809     condition *first_cond, *last_cond;
00810     condition *cond, *next_cond;
00811     condition *min_cost_conds, *chosen;
00812     long cost = 0, min_cost;
00813     list *new_vars;
00814 
00815     remaining_conds = *top_of_conds;
00816     first_cond = NIL;
00817     last_cond = NIL;
00818     new_vars = NIL;
00819 
00820     /* repeat:  scan through remaining_conds
00821        rate each one
00822        if tie, call lookahead routine
00823        add min-cost item to conds
00824      */
00825 
00826     while (remaining_conds) {
00827         /* --- find min-cost set --- */
00828         min_cost_conds = NIL;
00829         min_cost = 0;
00830         for (cond = remaining_conds; cond != NIL; cond = cond->next) {
00831             cost = cost_of_adding_condition(cond, bound_vars_tc_number, roots);
00832             if ((!min_cost_conds) || (cost < min_cost)) {
00833                 min_cost = cost;
00834                 min_cost_conds = cond;
00835                 cond->reorder.next_min_cost = NIL;
00836             } else if (cost == min_cost) {
00837                 cond->reorder.next_min_cost = min_cost_conds;
00838                 min_cost_conds = cond;
00839             }
00840             /* if (min_cost <= 1) break;  This optimization needs to be removed,
00841                otherwise the tie set is not created.
00842                Without the tie set we can't check the
00843                canonical order. */
00844         }
00845         /* --- if min_cost==MAX_COST, print error message --- */
00846         if ((min_cost == MAX_COST) && current_agent(sysparams)[PRINT_WARNINGS_SYSPARAM]) {
00847             print("Warning:  in production %s,\n", name_of_production_being_reordered);
00848             print("     The LHS conditions are not all connected.\n");
00849             /* BUGBUG I'm not sure whether this can ever happen. */
00850         }
00851         /* --- if more than one min-cost item, and cost>1, do lookahead --- */
00852         if ((min_cost > 1) && (min_cost_conds->reorder.next_min_cost)) {
00853             min_cost = MAX_COST + 1;
00854             for (cond = min_cost_conds, next_cond = cond->reorder.next_min_cost;
00855                  cond != NIL; cond = next_cond, next_cond = (cond ? cond->reorder.next_min_cost : NIL)) {
00856                 cost = find_lowest_cost_lookahead(remaining_conds, cond, bound_vars_tc_number, roots);
00857                 if (cost < min_cost) {
00858                     min_cost = cost;
00859                     min_cost_conds = cond;
00860                     cond->reorder.next_min_cost = NIL;
00861                 } else {
00862 /*******************************************************************
00863  These code segments find the condition in the tie set with the smallest
00864  value in the canonical order. This ensures that productions with the
00865  same set of conditions are ordered the same. Except if the variables
00866  are assigned differently.
00867 *********************************************************************/
00868                     if (cost == min_cost && cond->type == POSITIVE_CONDITION) {
00869                         if (canonical_cond_greater(min_cost_conds, cond)) {
00870                             min_cost = cost;
00871                             min_cost_conds = cond;
00872                             cond->reorder.next_min_cost = NIL;
00873                         }
00874                     }
00875                 }
00876 /*******************************************************************/
00877 
00878             }
00879         }
00880 /*******************************************************************/
00881         if (min_cost == 1 && (min_cost_conds->reorder.next_min_cost)) {
00882             for (cond = min_cost_conds; cond != NIL; cond = cond->reorder.next_min_cost) {
00883                 if (cond->type == POSITIVE_CONDITION &&
00884                     min_cost_conds->type == POSITIVE_CONDITION && canonical_cond_greater(min_cost_conds, cond)) {
00885                     min_cost = cost;
00886                     min_cost_conds = cond;
00887                 } else if (cond->type != POSITIVE_CONDITION && min_cost_conds->type == POSITIVE_CONDITION) {
00888                     min_cost = cost;
00889                     min_cost_conds = cond;
00890                 }
00891             }
00892         }
00893 /*******************************************************************/
00894 
00895         /* --- install the first item in the min-cost set --- */
00896         chosen = min_cost_conds;
00897         remove_from_dll(remaining_conds, chosen, next, prev);
00898         if (!first_cond)
00899             first_cond = chosen;
00900         /* Note: args look weird on the next line, because we're really
00901            inserting the chosen item at the *end* of the list */
00902         insert_at_head_of_dll(last_cond, chosen, prev, next);
00903 
00904         /* --- if a conjunctive negation, recursively reorder its conditions --- */
00905         if ((chosen->type == CONJUNCTIVE_NEGATION_CONDITION) && reorder_nccs) {
00906             list *ncc_roots;
00907             ncc_roots = collect_root_variables(chosen->data.ncc.top, bound_vars_tc_number, TRUE);
00908             reorder_condition_list(&(chosen->data.ncc.top),
00909                                    &(chosen->data.ncc.bottom), ncc_roots, bound_vars_tc_number, reorder_nccs);
00910             free_list(ncc_roots);
00911         }
00912 
00913         /* --- update set of bound variables for newly added condition --- */
00914         add_bound_variables_in_condition(chosen, bound_vars_tc_number, &new_vars);
00915 
00916         /* --- if all roots are bound, set roots=NIL: don't need 'em anymore --- */
00917         if (roots) {
00918             cons *c;
00919             for (c = roots; c != NIL; c = c->rest)
00920                 if (((Symbol *) (c->first))->var.tc_num != bound_vars_tc_number)
00921                     break;
00922             if (!c)
00923                 roots = NIL;
00924         }
00925 
00926     }                           /* end of while (remaining_conds) */
00927 
00928     unmark_variables_and_free_list(new_vars);
00929     *top_of_conds = first_cond;
00930     *bottom_of_conds = last_cond;
00931 }
00932 
00933 void reorder_condition_list(condition ** top_of_conds, condition ** bottom_of_conds, list * roots, tc_number tc,        /* for vars bound outside */
00934                             bool reorder_nccs)
00935 {
00936     saved_test *saved_tests;
00937 
00938     saved_tests = simplify_condition_list(*top_of_conds);
00939     reorder_simplified_conditions(top_of_conds, bottom_of_conds, roots, tc, reorder_nccs);
00940     restore_and_deallocate_saved_tests(*top_of_conds, tc, saved_tests);
00941 }
00942 
00943 /* -------------------------------------------------------------
00944    Reorders the LHS.
00945 ------------------------------------------------------------- */
00946 
00947 /* SBH/MVP 6-24-94 */
00948 
00949 bool test_tests_for_root(test t, list * roots)
00950 {
00951 
00952     cons *c;
00953     complex_test *ct;
00954     Symbol *referent;
00955 
00956     /* Gather variables from test. */
00957 
00958     if (test_is_blank_test(t))
00959         return FALSE;
00960 
00961     if (test_is_blank_or_equality_test(t)) {
00962         referent = referent_of_equality_test(t);
00963         if ((referent->common.symbol_type == VARIABLE_SYMBOL_TYPE) && member_of_list(referent, roots))
00964             return TRUE;
00965         return FALSE;
00966     }
00967     ct = complex_test_from_test(t);
00968 
00969     switch (ct->type) {
00970     case GOAL_ID_TEST:
00971     case IMPASSE_ID_TEST:
00972     case DISJUNCTION_TEST:
00973         return FALSE;
00974         break;
00975 
00976     case CONJUNCTIVE_TEST:
00977         for (c = ct->data.conjunct_list; c != NIL; c = c->rest)
00978             if (test_tests_for_root(c->first, roots))
00979                 return TRUE;
00980         return FALSE;
00981         break;
00982 
00983     default:
00984         /* --- relational tests other than equality --- */
00985         referent = ct->data.referent;
00986         if ((referent->common.symbol_type == VARIABLE_SYMBOL_TYPE) && member_of_list(referent, roots))
00987             return TRUE;
00988         return FALSE;
00989         break;
00990     }
00991 }
00992 
00993 /* voigtjr: lhs_bottom never referenced in function, removed*/
00994 /*void remove_isa_state_tests_for_non_roots(condition **lhs_top, condition **lhs_bottom, list *roots)*/
00995 void remove_isa_state_tests_for_non_roots(condition ** lhs_top, list * roots)
00996 {
00997     condition *cond;
00998     bool a, b;
00999     test temp;
01000 
01001     a = FALSE;
01002     b = FALSE;
01003 
01004     for (cond = *lhs_top; cond != NIL; cond = cond->next) {
01005         if ((cond->type == POSITIVE_CONDITION) &&
01006             (test_is_complex_test(cond->data.tests.id_test)) &&
01007             (test_includes_goal_or_impasse_id_test(cond->data.tests.id_test,
01008                                                    TRUE, FALSE)) &&
01009             (!test_tests_for_root(cond->data.tests.id_test, roots))) {
01010             temp = cond->data.tests.id_test;
01011             cond->data.tests.id_test = copy_test_removing_goal_impasse_tests(temp, &a, &b);
01012             deallocate_test(temp);      /* RBD fixed memory leak 3/29/95 */
01013         }
01014     }
01015 }
01016 
01017 bool reorder_lhs(condition ** lhs_top, condition ** lhs_bottom, bool reorder_nccs)
01018 {
01019     tc_number tc;
01020     list *roots;
01021 
01022     tc = get_new_tc_number();
01023     /* don't mark any variables, since nothing is bound outside the LHS */
01024 
01025     roots = collect_root_variables(*lhs_top, tc, TRUE);
01026 
01027     /* SBH/MVP 6-24-94 Fix to include only root "STATE" test in the LHS of a chunk. */
01028     /* voigtjr: lhs_bottom never referenced in function, removed */
01029     /*if (roots) remove_isa_state_tests_for_non_roots(lhs_top,lhs_bottom,roots); */
01030     if (roots)
01031         remove_isa_state_tests_for_non_roots(lhs_top, roots);
01032 
01033     /* MVP 6-8-94 - fix provided by Bob */
01034     if (!roots) {
01035         condition *cond;
01036 
01037         for (cond = *lhs_top; cond != NIL; cond = cond->next) {
01038             if ((cond->type == POSITIVE_CONDITION) &&
01039                 (test_includes_goal_or_impasse_id_test(cond->data.tests.id_test, TRUE, FALSE))) {
01040                 add_bound_variables_in_test(cond->data.tests.id_test, tc, &roots);
01041                 if (roots)
01042                     break;
01043             }
01044         }
01045     }
01046 
01047     if (!roots) {
01048         print("Error:  in production %s,\n", name_of_production_being_reordered);
01049         print("        The LHS has no roots.\n");
01050         /* BUGBUG most people aren't going to understand this error message */
01051         return FALSE;
01052     }
01053 
01054     fill_in_vars_requiring_bindings(*lhs_top, tc);
01055     reorder_condition_list(lhs_top, lhs_bottom, roots, tc, reorder_nccs);
01056     remove_vars_requiring_bindings(*lhs_top);
01057     free_list(roots);
01058     return TRUE;
01059 }
01060 
01061 void init_reorderer(void)
01062 {                               /* called from init_production_utilities() */
01063     init_memory_pool(&current_agent(saved_test_pool), sizeof(saved_test), "saved test");
01064 }

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