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

consistency.c

Go to the documentation of this file.
00001 /*************************************************************************
00002  *
00003  *  file:  consistency.c
00004  *
00005  * =======================================================================
00006  *
00007  * Source code for Operand2/Waterfall specific functions in the kernel.
00008  *
00009  * =======================================================================
00010  *
00011  * Copyright 1995-2003 Carnegie Mellon University,
00012  *                                                                               University of Michigan,
00013  *                                                                               University of Southern California/Information
00014  *                                                                               Sciences Institute. All rights reserved.
00015  *                                                                              
00016  * Redistribution and use in source and binary forms, with or without
00017  * modification, are permitted provided that the following conditions are met:
00018  *
00019  * 1.   Redistributions of source code must retain the above copyright notice,
00020  *              this list of conditions and the following disclaimer. 
00021  * 2.   Redistributions in binary form must reproduce the above copyright notice,
00022  *              this list of conditions and the following disclaimer in the documentation
00023  *              and/or other materials provided with the distribution. 
00024  *
00025  * THIS SOFTWARE IS PROVIDED BY THE SOAR CONSORTIUM ``AS IS'' AND ANY EXPRESS OR
00026  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
00027  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
00028  * EVENT SHALL THE SOAR CONSORTIUM  OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
00029  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00030  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00031  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00032  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00033  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00034  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00035  * The views and conclusions contained in the software and documentation are
00036  * those of the authors and should not be interpreted as representing official
00037  * policies, either expressed or implied, of Carnegie Mellon University, the
00038  * University of Michigan, the University of Southern California/Information
00039  * Sciences Institute, or the Soar consortium.
00040  * =======================================================================
00041  *
00042  * Revision history:
00043  * 
00044  * 05 May 97: Created for version 2.0 of Operand2
00045  * REW
00046  *
00047  * 20 Aug 97: Version 2.1 of Operand2/Waterfall
00048  * Reimplemented the Waterfall functions with a more efficient algorithm
00049  * REW
00050  *
00051  */
00052 
00053 /* For information on the consistency check routines */
00054 /* #define DEBUG_CONSISTENCY_CHECK    */
00055 
00056 /* For information on aspects of determining the active level */
00057 /* #define DEBUG_DETERMINE_LEVEL_PHASE   */
00058 
00059 #include "soarkernel.h"
00060 
00061 /* REW: begin 08.20.97 */
00062 #define NEW_DECISION         0
00063 #define SAME_LEVEL           1
00064 #define HIGHER_LEVEL         2
00065 #define LOWER_LEVEL          3
00066 #define NIL_GOAL_RETRACTIONS 4
00067 /* REW: end   08.20.97 */
00068 
00069 /* REW: begin 09.15.96 */
00070 extern void remove_wmes_for_context_slot(slot * s);
00071 extern void remove_existing_context_and_descendents(Symbol * goal);
00072 extern byte type_of_existing_impasse(Symbol * goal);
00073 extern Symbol *attribute_of_existing_impasse(Symbol * goal);
00074 extern byte run_preference_semantics_for_consistency_check(slot * s, preference ** result_candidates);
00075 
00076 void remove_operator_if_necessary(slot * s, wme * w);
00077 bool decision_consistent_with_current_preferences(Symbol * goal, slot * s);
00078 void remove_current_decision(slot * s);
00079 bool check_context_slot_decisions(goal_stack_level level);
00080 /* REW: end   09.15.96 */
00081 
00082                            /* REW: begin 08.20.97 *//* To implement the Waterfall part of Operand2 */
00083 extern void print_assertion(ms_change * msc);
00084 extern void print_retraction(ms_change * msc);
00085 void initialize_consistency_calculations_for_new_decision();
00086 void determine_highest_active_production_level_in_stack_apply();
00087 void determine_highest_active_production_level_in_stack_propose();
00088 bool goal_stack_consistent_through_goal(Symbol * goal);
00089 bool i_activity_at_goal(Symbol * goal);
00090 bool minor_quiescence_at_goal(Symbol * goal);
00091 int active_production_type_at_goal(Symbol * goal);
00092 Symbol *highest_active_goal_propose();
00093 Symbol *highest_active_goal_apply();
00094 /* REW: end   08.20.97 */
00095 
00096 void remove_operator_if_necessary(slot * s, wme * w)
00097 {
00098 
00099     /* REW: begin 11.25.96 */
00100 #ifndef NO_TIMING_STUFF
00101 #ifdef DETAILED_TIMING_STATS
00102     start_timer(&current_agent(start_gds_tv));
00103 #endif
00104 #endif
00105     /* REW: end   11.25.96 */
00106 
00107     /*         print("Examining slot (next)\n");
00108        for (next = s; next; next=next->next){
00109        print_with_symbols("Slot ID:   [%y]\n", next->id);
00110        print_with_symbols("Slot Attr: [%y]\n", next->attr);
00111        }
00112 
00113        print("Examining slot (prev)\n");
00114        for (prev = s->prev; prev; prev=prev->prev){
00115        print_with_symbols("Slot ID:   [%y]\n", prev->id);
00116        print_with_symbols("Slot Attr: [%y]\n", prev->attr);
00117        }
00118 
00119        print("Examining slot WMEs\n");
00120        for (slot_wmes=s->wmes; slot_wmes; slot_wmes=slot_wmes->next){
00121        print_wme(slot_wmes);
00122        }
00123 
00124        print("Examining acceptable preference WMEs\n");
00125        for (slot_wmes=s->acceptable_preference_wmes; slot_wmes; slot_wmes=slot_wmes->next){
00126        print_wme(slot_wmes);
00127        }
00128 
00129        if (current_agent(highest_goal_whose_context_changed)) print_with_symbols("Highest goal with changed context: [%y]\n", current_agent(highest_goal_whose_context_changed));
00130 
00131        print_with_symbols("Slot ID:   [%y]\n", s->id);
00132        print_with_symbols("Slot Attr: [%y]\n", s->attr);
00133        if (s->isa_context_slot) print("this is a context slot.\n");
00134        if (s->impasse_id) print_with_symbols("Impasse: [%y]\n", s->impasse_id);
00135        if (s->acceptable_preference_changed) print("Acceptable pref changed\n");
00136 
00137        print_with_symbols("WME ID:    [%y]\n", w->id);
00138        print_with_symbols("WME Attr:  [%y]\n", w->attr);
00139        print_with_symbols("WME Value: [%y]\n", w->value);
00140        if (w->value->id.isa_operator) print("This is an operator\n");
00141 
00142        print_with_symbols("s->id->id.operator_slot->id: [%y]\n", s->id->id.operator_slot->id); */
00143 
00144     if (s->wmes) {              /* If there is something in the context slot */
00145         if (s->wmes->value == w->value) {       /* The WME in the context slot is WME whose pref changed */
00146 
00147 #ifndef TRACE_CONTEXT_DECISIONS_ONLY
00148 
00149             if (current_agent(sysparams)[TRACE_OPERAND2_REMOVALS_SYSPARAM]) {
00150                 print("\n        REMOVING: Operator from context slot (proposal no longer matches): ");
00151                 print_wme(w);
00152             }
00153 #endif
00154             remove_wmes_for_context_slot(s);
00155             if (s->id->id.lower_goal)
00156                 remove_existing_context_and_descendents(s->id->id.lower_goal);
00157         }
00158     }
00159 
00160     /* REW: begin 11.25.96 */
00161 #ifndef NO_TIMING_STUFF
00162 #ifdef DETAILED_TIMING_STATS
00163     stop_timer(&current_agent(start_gds_tv), &current_agent(gds_cpu_time[current_agent(current_phase)]));
00164 #endif
00165 #endif
00166     /* REW: end   11.25.96 */
00167 }
00168 
00169 /* This code concerns the implementation of a 'consistency check' following
00170    each IE phase.  The basic idea is that we want context decisions to 
00171    remain consistent with the current preferences, even if the proposal
00172    for some operator is still acceptable */
00173 
00174 bool decision_consistent_with_current_preferences(Symbol * goal, slot * s)
00175 {
00176     byte current_impasse_type, new_impasse_type;
00177     Symbol *current_impasse_attribute;
00178     wme *current_operator;
00179     preference *candidates, *cand;
00180     bool operator_in_slot, goal_is_impassed;
00181 
00182 #ifdef DEBUG_CONSISTENCY_CHECK
00183     if (s->isa_context_slot) {
00184         print("    slot (s)  isa context slot: ");
00185         print_with_symbols("    Slot Identifier [%y] and attribute [%y]\n", s->id, s->attr);
00186     }
00187     /* print("    Address of s: %x\n", s); */
00188     print("    s->impasse_type: %d\n", s->impasse_type);
00189     if (s->impasse_id)
00190         print("    Impasse ID is set (non-NIL)\n");
00191 #endif
00192 
00193     /* Determine the current operator/impasse in the slot */
00194     if (goal->id.operator_slot->wmes) {
00195         /* There is an operator in the slot */
00196         current_operator = goal->id.operator_slot->wmes;
00197         operator_in_slot = TRUE;
00198     } else {
00199         /* There is not an operator in the slot */
00200         current_operator = NIL;
00201         operator_in_slot = FALSE;
00202     }
00203 
00204     if (goal->id.lower_goal) {
00205         /* the goal is impassed */
00206         goal_is_impassed = TRUE;
00207         current_impasse_type = type_of_existing_impasse(goal);
00208         current_impasse_attribute = attribute_of_existing_impasse(goal);
00209 #ifdef DEBUG_CONSISTENCY_CHECK
00210         print("    Goal is impassed:  Impasse type: %d: ", current_impasse_type);
00211         print_with_symbols("    Impasse attribute: [%y]\n", current_impasse_attribute);
00212 #endif
00213         /* Special case for an operator no-change */
00214         if ((operator_in_slot) && (current_impasse_type == NO_CHANGE_IMPASSE_TYPE)) {
00215             /* Operator no-change impasse: run_preference_semantics will return 0
00216                and we only want to blow away this operator if another is better
00217                than it (checked in NONE_IMPASSE_TYPE switch) or if another kind
00218                of impasse would be generated (e.g., OPERATOR_TIE). So, we set
00219                the impasse type here to 0; that way we'll know that we should be
00220                comparing a previous decision for a unique operator against the
00221                current preference semantics. */
00222 #ifdef DEBUG_CONSISTENCY_CHECK
00223             print("    This is an operator no-change  impasse.\n");
00224 #endif
00225             current_impasse_type = NONE_IMPASSE_TYPE;
00226         }
00227     } else {
00228         goal_is_impassed = FALSE;
00229         current_impasse_type = NONE_IMPASSE_TYPE;
00230         current_impasse_attribute = NIL;
00231 #ifdef DEBUG_CONSISTENCY_CHECK
00232         print("    Goal is not impassed: ");
00233 #endif
00234     }
00235 
00236     /* Determine the new impasse type, based on the preferences that exist now */
00237     new_impasse_type = run_preference_semantics_for_consistency_check(s, &candidates);
00238 
00239 #ifdef DEBUG_CONSISTENCY_CHECK
00240     print("    Impasse Type returned by run preference semantics: %d\n", new_impasse_type);
00241 
00242     for (cand = candidates; cand; cand = cand->next) {
00243         print("    Preference for slot:");
00244         print_preference(cand);
00245     }
00246 
00247     for (cand = candidates; cand; cand = cand->next_candidate) {
00248         print("\n    Candidate  for slot:");
00249         print_preference(cand);
00250     }
00251 #endif
00252 
00253     if (current_impasse_type != new_impasse_type) {
00254         /* Then there is an inconsistency: no more work necessary */
00255 #ifdef DEBUG_CONSISTENCY_CHECK
00256         print
00257             ("    Impasse types are different: Returning FALSE, preferences are not consistent with prior decision.\n");
00258 #endif
00259         return FALSE;
00260     }
00261 
00262     /* in these cases, we know that the new impasse and the old impasse *TYPES* are the same.  We
00263        just want to check and make the actual impasses/decisions are the same. */
00264     switch (new_impasse_type) {
00265 
00266     case NONE_IMPASSE_TYPE:
00267         /* There are four cases to consider when NONE_IMPASSE_TYPE is returned: */
00268         /* 1.  Previous operator and operator returned by run_pref_sem are the same.
00269            In this case, return TRUE (decision remains consistent) */
00270 
00271         /* This next if is meant to test that there actually is something in the slot but
00272            I'm nut quite certain that it will not always be true? */
00273         if (operator_in_slot) {
00274 #ifdef DEBUG_CONSISTENCY_CHECK
00275             print("    There is a WME in the operator slot:");
00276             print_wme(current_operator);
00277 #endif
00278 
00279             /* Because of indifferent preferences, we need to compare all possible candidates
00280                with the current decision */
00281             for (cand = candidates; cand; cand = cand->next_candidate) {
00282                 if (current_operator->value == cand->value) {
00283 #ifdef DEBUG_CONSISTENCY_CHECK
00284                     print_with_symbols("       Operator slot ID [%y] and candidate ID [%y] are the same.\n",
00285                                        current_operator->value, cand->value);
00286 #endif
00287                     return TRUE;
00288                 }
00289             }
00290 
00291             /* 2.  A different operator is indicated for the slot than the one that is
00292                currently installed.  In this case, we return FALSE (the decision is
00293                not consistent with the preferences). */
00294 
00295             /* Now we know that the decision is inconsistent */
00296             return FALSE;
00297 
00298             /* 3.  A single operator is suggested when an impasse existed previously.
00299                In this case, return FALSE so that the impasse can be removed. */
00300 
00301         } else {                /* There is no operator in the slot */
00302             if (goal->id.lower_goal) {  /* But there is an impasse */
00303                 if (goal->id.lower_goal->id.isa_impasse)
00304                     print("This goal is an impasse\n");
00305                 print("      No Impasse Needed but Impasse exists: remove impasse now\n");
00306                 print("\n\n   *************This should never be executed*******************\n\n");
00307                 return FALSE;
00308             }
00309         }
00310 
00311         /* 4.  This is the bottom goal in the stack and there is no operator or
00312            impasse for the operator slot created yet.  We shouldn't call this
00313            routine in this case (this condition is checked before  
00314            decision_consistent_with_current_preferences is called) but, for
00315            completeness' sake, we check this condition and return TRUE
00316            (because no decision has been made at this level, there is no 
00317            need to remove anything). */
00318         print("\n\n   *************This should never be executed*******************\n\n");
00319         return TRUE;
00320         break;
00321 
00322     case CONSTRAINT_FAILURE_IMPASSE_TYPE:
00323 #ifdef DEBUG_CONSISTENCY_CHECK
00324         print("    Constraint Failure Impasse: Returning TRUE\n");
00325 #endif
00326         return TRUE;
00327         break;
00328 
00329     case CONFLICT_IMPASSE_TYPE:
00330 #ifdef DEBUG_CONSISTENCY_CHECK
00331         print("    Conflict Impasse: Returning TRUE\n");
00332 #endif
00333         return TRUE;
00334         break;
00335 
00336     case TIE_IMPASSE_TYPE:
00337 #ifdef DEBUG_CONSISTENCY_CHECK
00338         print("    Tie Impasse: Returning TRUE\n");
00339 #endif
00340         return TRUE;
00341         break;
00342 
00343     case NO_CHANGE_IMPASSE_TYPE:
00344 #ifdef DEBUG_CONSISTENCY_CHECK
00345         print("    No change Impasse: Returning TRUE\n");
00346 #endif
00347         return TRUE;
00348         break;
00349     }
00350 
00351     print("\n   After switch................");
00352     print("\n\n   *************This should never be executed*******************\n\n");
00353     return TRUE;
00354 
00355 }
00356 
00357 void remove_current_decision(slot * s)
00358 {
00359 
00360 #ifndef TRACE_CONTEXT_DECISIONS_ONLY
00361 
00362     if (!s->wmes)
00363         if (current_agent(sysparams)[TRACE_OPERAND2_REMOVALS_SYSPARAM])
00364             print_with_symbols("\n       REMOVING CONTEXT SLOT: Slot Identifier [%y] and attribute [%y]\n", s->id,
00365                                s->attr);
00366 
00367     if (s->id)
00368         if (current_agent(sysparams)[TRACE_OPERAND2_REMOVALS_SYSPARAM])
00369             print_with_symbols("\n          Decision for goal [%y] is inconsistent.  Replacing it with....\n", s->id);
00370 
00371 #endif
00372 
00373     /* If there is an operator in the slot, remove it */
00374     remove_wmes_for_context_slot(s);
00375 
00376     /* If there are any subgoals, remove those */
00377     if (s->id->id.lower_goal)
00378         remove_existing_context_and_descendents(s->id->id.lower_goal);
00379 
00380     do_buffered_wm_and_ownership_changes();
00381 
00382 }
00383 
00384 /* ------------------------------------------------------------------
00385                        Check Context Slot Decisions
00386 
00387    This scans down the goal stack and checks the consistency of the current
00388    decision versus the current preferences for the slot, if the preferences
00389    have changed.
00390 ------------------------------------------------------------------ */
00391 
00392 bool check_context_slot_decisions(goal_stack_level level)
00393 {
00394     Symbol *goal;
00395     slot *s;
00396 
00397 #ifdef DEBUG_CONSISTENCY_CHECK
00398     if (current_agent(highest_goal_whose_context_changed))
00399         print_with_symbols("    Highest goal with changed context: [%y]\n",
00400                            current_agent(highest_goal_whose_context_changed));
00401 #endif
00402 
00403 /* REW: begin 05.05.97 */
00404     /* Check only those goals where preferences have changes that are at or above the level 
00405        of the consistency check */
00406     for (goal = current_agent(highest_goal_whose_context_changed); goal && goal->id.level <= level;
00407          goal = goal->id.lower_goal) {
00408 /* REW: end   05.05.97 */
00409 #ifdef DEBUG_CONSISTENCY_CHECK
00410         print_with_symbols("    Looking at goal [%y] to see if its preferences have changed\n", goal);
00411 #endif
00412         s = goal->id.operator_slot;
00413 
00414         if ((goal->id.lower_goal) || (s->wmes)) {       /* If we are not at the bottom goal or if there is an operator in the
00415                                                            bottom goal's operator slot */
00416 #ifdef DEBUG_CONSISTENCY_CHECK
00417             print
00418                 ("      This is a goal that either has subgoals or, if the bottom goal, has an operator in the slot\n");
00419 #endif
00420             if (s->changed) {   /* Only need to check a goal if its prefs have changed */
00421 #ifdef DEBUG_CONSISTENCY_CHECK
00422                 print("      This goal's preferences have changed.\n");
00423 #endif
00424                 if (!decision_consistent_with_current_preferences(goal, s)) {
00425 #ifdef DEBUG_CONSISTENCY_CHECK
00426                     print_with_symbols
00427                         ("   The current preferences indicate that the decision at [%y] needs to be removed.\n", goal);
00428 #endif
00429                     /* This doesn;t seem like it should be necessary but evidently it is: see 2.008 */
00430                     remove_current_decision(s);
00431                     return FALSE;
00432                     break;      /* No need to continue once a decision is removed */
00433                 }
00434             }
00435         }
00436 #ifdef DEBUG_CONSISTENCY_CHECK
00437         else {
00438             printf("   This is a bottom goal with no operator in the slot\n");
00439         }
00440 #endif
00441     }
00442 
00443     return TRUE;
00444 }
00445 
00446 /* REW: begin 08.20.97 */
00447 
00448 bool i_activity_at_goal(Symbol * goal)
00449 {
00450 
00451     /* print_with_symbols("\nLooking for I-activity at goal: %y\n", goal); */
00452 
00453     if (goal->id.ms_i_assertions)
00454         return TRUE;
00455 
00456     if (goal->id.ms_retractions)
00457         return TRUE;
00458 
00459     /* print("\nNo instantiation found.  Returning FALSE\n");  */
00460     return FALSE;
00461 }
00462 
00463 /*   Minor Quiescence at GOAL
00464 
00465      This procedure returns TRUE if the current firing type is IE_PRODS and
00466      there are no i-assertions (or any retractions) ready to fire in the
00467      current GOAL.  Else it returns FALSE.  */
00468 
00469 bool minor_quiescence_at_goal(Symbol * goal)
00470 {
00471 
00472     if ((current_agent(FIRING_TYPE) == IE_PRODS) && (!i_activity_at_goal(goal)))
00473         /* firing IEs but no more to fire == minor quiescence */
00474         return TRUE;
00475     else
00476         return FALSE;
00477 }
00478 
00479 /* ---------------------------------------------------------------------- */
00480 /* Find the highest goal of activity among the current assertions and
00481  * retractions */
00482 
00483 /* We have to start at the top of the goal stack and go down because *any*
00484  * goal in the goal stack could be active (and we want to highest one).
00485  * However, we terminate as soon as a goal with assertions or retractions
00486  * is found.  Propose cares only about ms_i_assertions & retractions
00487  */
00488 
00489 Symbol *highest_active_goal_propose()
00490 {
00491 
00492     Symbol *goal;
00493 
00494     for (goal = current_agent(top_goal); goal; goal = goal->id.lower_goal) {
00495 
00496 #ifdef DEBUG_DETERMINE_LEVEL_PHASE
00497         /* Debugging only */
00498         print("In highest_active_goal_propose:\n");
00499         if (goal->id.ms_i_assertions)
00500             print_assertion(goal->id.ms_i_assertions);
00501         if (goal->id.ms_retractions)
00502             print_retraction(goal->id.ms_retractions);
00503 #endif
00504 
00505         /* If there are any active productions at this goal, return the goal */
00506         if ((goal->id.ms_i_assertions) || (goal->id.ms_retractions))
00507             return goal;
00508     }
00509 
00510     /* This routine should only be called when !quiescence.  However, there is
00511        still the possibility that the only active productions are retractions
00512        that matched in a NIL goal.  If so, then we just return the bottom goal.
00513        If not, then all possibilities have been exausted and we have encounted
00514        an unrecoverable error. */
00515 
00516 #ifdef DEBUG_DETERMINE_LEVEL_PHASE
00517     print("WARNING: Returning NIL active goal because only NIL goal retractions are active.");
00518 #endif
00519     if (current_agent(nil_goal_retractions))
00520         return NIL;
00521     {
00522         char msg[MESSAGE_SIZE];
00523         strncpy(msg, "\n consistency.c: Error: Unable to find an active goal when not at quiescence.\n", MESSAGE_SIZE);
00524         msg[MESSAGE_SIZE - 1] = 0;
00525         abort_with_fatal_error(msg);
00526     }
00527     return NIL;                 /* unneeded, but avoids gcc -Wall warning */
00528 }
00529 
00530 Symbol *highest_active_goal_apply()
00531 {
00532 
00533     Symbol *goal;
00534 
00535     for (goal = current_agent(top_goal); goal; goal = goal->id.lower_goal) {
00536 
00537 #ifdef DEBUG_DETERMINE_LEVEL_PHASE
00538         /* Debugging only */
00539         print("In highest_active_goal_apply :\n");
00540         if (goal->id.ms_i_assertions)
00541             print_assertion(goal->id.ms_i_assertions);
00542         if (goal->id.ms_o_assertions)
00543             print_assertion(goal->id.ms_o_assertions);
00544         if (goal->id.ms_retractions)
00545             print_retraction(goal->id.ms_retractions);
00546 #endif
00547 
00548         /* If there are any active productions at this goal, return the goal */
00549         if ((goal->id.ms_i_assertions) || (goal->id.ms_o_assertions)
00550             || (goal->id.ms_retractions))
00551             return goal;
00552     }
00553 
00554     /* This routine should only be called when !quiescence.  However, there is
00555        still the possibility that the only active productions are retractions
00556        that matched in a NIL goal.  If so, then we just return the bottom goal.
00557        If not, then all possibilities have been exausted and we have encounted
00558        an unrecoverable error. */
00559 
00560 #ifdef DEBUG_DETERMINE_LEVEL_PHASE
00561     print("WARNING: Returning NIL active goal because only NIL goal retractions are active.");
00562 #endif
00563     if (current_agent(nil_goal_retractions))
00564         return NIL;
00565     {
00566         char msg[MESSAGE_SIZE];
00567         strncpy(msg, "\nconsistency.c: Error: Unable to find an active goal when not at quiescence.\n", MESSAGE_SIZE);
00568         msg[MESSAGE_SIZE - 1] = 0;
00569         abort_with_fatal_error(msg);
00570     }
00571     return NIL;                 /* unneeded, but avoids gcc -Wall warning */
00572 }
00573 
00574 /* ---------------------------------------------------------------------- */
00575 
00576 /* active_production_type_at_goal
00577    
00578    Determines type of productions active at some  active level.  If
00579    IE PRODS are active, this value is returned (regardless of whether there
00580    are PEs active or not). Note that this procedure will return erroneous
00581    values if there is no activity at the current level.  It should only be
00582    called when activity at the active_level has been determined. */
00583 
00584 int active_production_type_at_goal(Symbol * goal)
00585 {
00586 
00587     if (i_activity_at_goal(goal))
00588         return IE_PRODS;
00589     else
00590         return PE_PRODS;
00591 }
00592 
00593 /* ---------------------------------------------------------------------- */
00594 
00595 bool goal_stack_consistent_through_goal(Symbol * goal)
00596 {
00597     bool test;
00598 
00599 #ifndef NO_TIMING_STUFF
00600 #ifdef DETAILED_TIMING_STATS
00601     start_timer(&current_agent(start_gds_tv));
00602 #endif
00603 #endif
00604 
00605 #ifdef DEBUG_CONSISTENCY_CHECK
00606     print("\nStart: CONSISTENCY CHECK at level %d\n", goal->id.level);
00607 
00608     /* Just a bunch of debug stuff for now */
00609     if (current_agent(highest_goal_whose_context_changed)) {
00610         print_with_symbols("current_agent(highest_goal_whose_context_changed) = [%y]\n",
00611                            current_agent(highest_goal_whose_context_changed));
00612     } else {
00613         print("Evidently, nothing has changed: not checking slots\n");
00614     }
00615 #endif
00616 
00617     test = check_context_slot_decisions(goal->id.level);
00618 
00619 #ifdef DEBUG_CONSISTENCY_CHECK
00620     print("\nEnd:   CONSISTENCY CHECK\n");
00621 #endif
00622 
00623 #ifdef DETAILED_TIMING_STATS
00624     stop_timer(&current_agent(start_gds_tv), &current_agent(gds_cpu_time[current_agent(current_phase)]));
00625 #endif
00626 
00627     return test;
00628 }
00629 
00630 /* REW: end   08.20.97 */
00631 
00632 /* ---------------------------------------------------------------------- */
00633 
00634 /* REW: begin 05.05.97 */
00635 
00636 void initialize_consistency_calculations_for_new_decision()
00637 {
00638 
00639     Symbol *goal;
00640 
00641 #ifdef DEBUG_DETERMINE_LEVEL_PHASE
00642     print("\nInitialize consistency calculations for new decision.\n");
00643 #endif
00644 
00645     /* No current activity level */
00646     current_agent(active_level) = 0;
00647     current_agent(active_goal) = NIL;
00648 
00649     /* Clear any interruption flags on the goals.... */
00650     for (goal = current_agent(top_goal); goal; goal = goal->id.lower_goal)
00651         goal->id.saved_firing_type = NO_SAVED_PRODS;
00652 }
00653 
00654 /* ---------------------------------------------------------------------- */
00655 
00656    /* determine_highest_active_production_level_in_stack_apply()
00657 
00658       This routine is responsible for implementing the DETERMINE_LEVEL_PHASE.
00659       In the Waterfall version of Soar, the DETERMINE_LEVEL_PHASE makes the
00660       determination of what goal level is active in the stack.  Activity
00661       proceeds from top goal to bottom goal so the active goal is the goal
00662       highest in the stack with productions waiting to fire.  This procedure
00663       also recognizes quiescence (no productions active anywhere) and
00664       mini-quiescence (no more IE_PRODS are waiting to fire in some goal for a
00665       goal that fired IE_PRODS in the previous elaboration).  Mini-quiescence is
00666       followed by a consistency check. */
00667 
00668 void determine_highest_active_production_level_in_stack_apply()
00669 {
00670 
00671     Symbol *goal;
00672     int level_change_type, diff;
00673 
00674 #ifndef TRACE_CONTEXT_DECISIONS_ONLY
00675     if (current_agent(sysparams)[TRACE_PHASES_SYSPARAM])
00676         print("\n--- Application Phase ---\n");
00677 #endif
00678 
00679 #ifdef DEBUG_DETERMINE_LEVEL_PHASE
00680     print("\nDetermining the highest active level in the stack....\n");
00681 #endif
00682 
00683     if (!any_assertions_or_retractions_ready()) {
00684         /* This is quiescence */
00685 #ifdef DEBUG_DETERMINE_LEVEL_PHASE
00686         print("\n(Full) Quiescence has been reached...going to decision\n");
00687 #endif
00688 
00689         /* Need to determine if this quiescence is also a minor quiescence,
00690            otherwise, an inconsistent decision could get retained here (because
00691            the consistency check was never run). (2.008).  Therefore, if
00692            in the previous preference phase, IE_PRODS fired, then force a 
00693            consistency check over the entire stack (by checking at the
00694            bottom goal). */
00695 
00696         if (minor_quiescence_at_goal(current_agent(bottom_goal))) {
00697             goal_stack_consistent_through_goal(current_agent(bottom_goal));
00698         }
00699 
00700         /* regardless of the outcome, we go to the decision phase */
00701 
00702         current_agent(current_phase) = OUTPUT_PHASE;
00703         return;
00704     }
00705 
00706     /* Not Quiescence */
00707 
00708     /* Check for Max ELABORATIONS EXCEEDED */
00709 
00710     if (current_agent(e_cycles_this_d_cycle) >= (unsigned long) (current_agent(sysparams)[MAX_ELABORATIONS_SYSPARAM])) {
00711         if (current_agent(sysparams)[PRINT_WARNINGS_SYSPARAM])
00712             print("\nWarning: reached max-elaborations; proceeding to decision phase.");
00713         current_agent(current_phase) = OUTPUT_PHASE;
00714         return;
00715     }
00716 
00717     /* Save the old goal and level (must save level explicitly in case goal is NIL) */
00718     current_agent(previous_active_goal) = current_agent(active_goal);
00719     current_agent(previous_active_level) = current_agent(active_level);
00720 
00721     /* Determine the new highest level of activity */
00722     current_agent(active_goal) = highest_active_goal_apply();
00723     if (current_agent(active_goal))
00724         current_agent(active_level) = current_agent(active_goal)->id.level;
00725     else
00726         current_agent(active_level) = 0;        /* Necessary for get_next_retraction */
00727 
00728 #ifdef DEBUG_DETERMINE_LEVEL_PHASE
00729     print("\nHighest level of activity is....%d", current_agent(active_level));
00730     print("\n   Previous level of activity is....%d", current_agent(previous_active_level));
00731 #endif
00732 
00733     if (!current_agent(active_goal))
00734         /* Only NIL goal retractions */
00735         level_change_type = NIL_GOAL_RETRACTIONS;
00736     else if (current_agent(previous_active_level) == 0)
00737         level_change_type = NEW_DECISION;
00738     else {
00739         diff = current_agent(active_level) - current_agent(previous_active_level);
00740         if (diff == 0)
00741             level_change_type = SAME_LEVEL;
00742         else if (diff > 0)
00743             level_change_type = LOWER_LEVEL;
00744         else
00745             level_change_type = HIGHER_LEVEL;
00746     }
00747 
00748     switch (level_change_type) {
00749     case NIL_GOAL_RETRACTIONS:
00750 #ifdef DEBUG_DETERMINE_LEVEL_PHASE
00751         print("\nOnly NIL goal retractions are active");
00752 #endif
00753         current_agent(FIRING_TYPE) = IE_PRODS;
00754         current_agent(current_phase) = PREFERENCE_PHASE;
00755         break;
00756 
00757     case NEW_DECISION:
00758 #ifdef DEBUG_DETERMINE_LEVEL_PHASE
00759         print("\nThis is a new decision....");
00760 #endif
00761         current_agent(FIRING_TYPE) = active_production_type_at_goal(current_agent(active_goal));
00762 
00763         /* in APPLY phase, we can test for ONC here, check ms_o_assertions */
00764 
00765         current_agent(current_phase) = PREFERENCE_PHASE;
00766         break;
00767 
00768     case LOWER_LEVEL:
00769 #ifdef DEBUG_DETERMINE_LEVEL_PHASE
00770         print("\nThe level is lower than the previous level....");
00771 #endif
00772         /* Is there a minor quiescence at the previous level? */
00773         if (minor_quiescence_at_goal(current_agent(previous_active_goal))) {
00774 #ifdef DEBUG_DETERMINE_LEVEL_PHASE
00775             print("\nMinor quiescence at level %d", current_agent(previous_active_level));
00776 #endif
00777             if (!goal_stack_consistent_through_goal(current_agent(previous_active_goal))) {
00778                 current_agent(current_phase) = OUTPUT_PHASE;
00779                 break;
00780             }
00781         }
00782 
00783         /* else: check if return to interrupted level */
00784 
00785         goal = current_agent(active_goal);
00786 
00787 #ifdef DEBUG_DETERMINE_LEVEL_PHASE
00788         if (goal->id.saved_firing_type == IE_PRODS)
00789             print("\nSaved production type: IE _PRODS");
00790         if (goal->id.saved_firing_type == PE_PRODS)
00791             print("\nSaved production type: PE _PRODS");
00792         if (goal->id.saved_firing_type == NO_SAVED_PRODS)
00793             print("\nSaved production type: NONE");
00794 #endif
00795 
00796         if (goal->id.saved_firing_type != NO_SAVED_PRODS) {
00797 #ifdef DEBUG_DETERMINE_LEVEL_PHASE
00798             print("\nRestoring production type from previous processing at this level");
00799 #endif
00800             current_agent(FIRING_TYPE) = goal->id.saved_firing_type;
00801             current_agent(current_phase) = DETERMINE_LEVEL_PHASE;
00802             break;
00803         }
00804 
00805         /* else: just do a preference phase */
00806         current_agent(FIRING_TYPE) = active_production_type_at_goal(current_agent(active_goal));
00807         current_agent(current_phase) = PREFERENCE_PHASE;
00808         break;
00809 
00810     case SAME_LEVEL:
00811 #ifdef DEBUG_DETERMINE_LEVEL_PHASE
00812         print("\nThe level is the same as the previous level....");
00813 #endif
00814         if (minor_quiescence_at_goal(current_agent(active_goal))) {
00815 #ifdef DEBUG_DETERMINE_LEVEL_PHASE
00816             print("\nMinor quiescence at level %d", current_agent(active_level));
00817 #endif
00818             if (!goal_stack_consistent_through_goal(current_agent(active_goal))) {
00819                 current_agent(current_phase) = OUTPUT_PHASE;
00820                 break;
00821             }
00822         }
00823 
00824         current_agent(FIRING_TYPE) = active_production_type_at_goal(current_agent(active_goal));
00825         current_agent(current_phase) = PREFERENCE_PHASE;
00826         break;
00827 
00828     case HIGHER_LEVEL:
00829 #ifdef DEBUG_DETERMINE_LEVEL_PHASE
00830         print("\nThe level is higher than the previous level....");
00831 #endif
00832 
00833         goal = current_agent(previous_active_goal);
00834         goal->id.saved_firing_type = current_agent(FIRING_TYPE);
00835 
00836 #ifdef DEBUG_DETERMINE_LEVEL_PHASE
00837         if (goal->id.saved_firing_type == IE_PRODS)
00838             print("\n Saving current firing type as IE_PRODS");
00839         else if (goal->id.saved_firing_type == PE_PRODS)
00840             print("\n Saving current firing type as PE_PRODS");
00841         else if (goal->id.saved_firing_type == NO_SAVED_PRODS)
00842             print("\n Saving current firing type as NO_SAVED_PRODS");
00843         else
00844             print("\n Unknown SAVED firing type???????");
00845 #endif
00846 
00847         /* run consistency check at new active level *before* firing any
00848            productions there */
00849 
00850 #ifdef DEBUG_DETERMINE_LEVEL_PHASE
00851         print("\nMinor quiescence at level %d", current_agent(active_level));
00852 #endif
00853         if (!goal_stack_consistent_through_goal(current_agent(active_goal))) {
00854             current_agent(current_phase) = OUTPUT_PHASE;
00855             break;
00856         }
00857 
00858         /* If the decision is consistent, then just start processing at this level */
00859 
00860         current_agent(FIRING_TYPE) = active_production_type_at_goal(current_agent(active_goal));
00861         current_agent(current_phase) = PREFERENCE_PHASE;
00862         break;
00863     }
00864 
00865 }
00866 
00867 /* REW: end   05.05.97 */
00868 
00869                           /* KJC: begin 10.04.98 *//* swiped from REW's determine_highest_active... */
00870 /* ---------------------------------------------------------------------- */
00871 
00872    /* determine_highest_active_production_level_in_stack_propose()
00873 
00874       This routine is responsible for implementing the DETERMINE_LEVEL_PHASE
00875       for the Propose Phase under the new reordering of the Decision Cycle.
00876       In the Waterfall version of Soar, the DETERMINE_LEVEL_PHASE makes the
00877       determination of what goal level is active in the stack.  Activity
00878       proceeds from top goal to bottom goal so the active goal is the goal
00879       highest in the stack with productions waiting to fire.  This procedure
00880       also recognizes quiescence (no productions active anywhere) and
00881       mini-quiescence (no more IE_PRODS are waiting to fire in some goal for a
00882       goal that fired IE_PRODS in the previous elaboration).  Mini-quiescence is
00883       followed by a consistency check. */
00884 
00885 void determine_highest_active_production_level_in_stack_propose()
00886 {
00887 
00888     Symbol *goal;
00889     int level_change_type, diff;
00890 
00891 #ifndef TRACE_CONTEXT_DECISIONS_ONLY
00892     if (current_agent(sysparams)[TRACE_PHASES_SYSPARAM])
00893         print("\n--- Proposal Phase ---\n");
00894 #endif
00895 
00896 #ifdef DEBUG_DETERMINE_LEVEL_PHASE
00897     print("\n(Propose) Determining the highest active level in the stack....\n");
00898 #endif
00899 
00900     /* We are only checking for i_assertions, not o_assertions, since we don't
00901        want operators to fire in the proposal phase
00902      */
00903     if (!(current_agent(ms_retractions) || current_agent(ms_i_assertions))) {
00904         /*if (minor_quiescence_at_goal(current_agent(bottom_goal))) { */
00905         /* This is minor quiescence */
00906 #ifdef DEBUG_DETERMINE_LEVEL_PHASE
00907         print("\n Propose Phase Quiescence has been reached...going to decision\n");
00908 #endif
00909 
00910         /* Force a consistency check over the entire stack (by checking at
00911            the bottom goal). */
00912         goal_stack_consistent_through_goal(current_agent(bottom_goal));
00913 
00914         /* Decision phase is always next */
00915         current_agent(current_phase) = DECISION_PHASE;
00916         return;
00917     }
00918 
00919     /* Not Quiescence */
00920 
00921     /* Check for Max ELABORATIONS EXCEEDED */
00922 
00923     if (current_agent(e_cycles_this_d_cycle) >= (unsigned long) (current_agent(sysparams)[MAX_ELABORATIONS_SYSPARAM])) {
00924         if (current_agent(sysparams)[PRINT_WARNINGS_SYSPARAM])
00925             print("\nWarning: reached max-elaborations; proceeding to decision phase.");
00926         current_agent(current_phase) = DECISION_PHASE;
00927         return;
00928     }
00929 
00930     /* not Max Elaborations */
00931 
00932     /* Save the old goal and level (must save level explicitly in case
00933        goal is NIL) */
00934     current_agent(previous_active_goal) = current_agent(active_goal);
00935     current_agent(previous_active_level) = current_agent(active_level);
00936 
00937     /* Determine the new highest level of activity */
00938     current_agent(active_goal) = highest_active_goal_propose();
00939     if (current_agent(active_goal))
00940         current_agent(active_level) = current_agent(active_goal)->id.level;
00941     else
00942         current_agent(active_level) = 0;        /* Necessary for get_next_retraction */
00943 
00944 #ifdef DEBUG_DETERMINE_LEVEL_PHASE
00945     print("\nHighest level of activity is....%d", current_agent(active_level));
00946     print("\n   Previous level of activity is....%d", current_agent(previous_active_level));
00947 #endif
00948 
00949     if (!current_agent(active_goal))
00950         /* Only NIL goal retractions */
00951         level_change_type = NIL_GOAL_RETRACTIONS;
00952     else if (current_agent(previous_active_level) == 0)
00953         level_change_type = NEW_DECISION;
00954     else {
00955         diff = current_agent(active_level) - current_agent(previous_active_level);
00956         if (diff == 0)
00957             level_change_type = SAME_LEVEL;
00958         else if (diff > 0)
00959             level_change_type = LOWER_LEVEL;
00960         else
00961             level_change_type = HIGHER_LEVEL;
00962     }
00963 
00964     switch (level_change_type) {
00965     case NIL_GOAL_RETRACTIONS:
00966 #ifdef DEBUG_DETERMINE_LEVEL_PHASE
00967         print("\nOnly NIL goal retractions are active");
00968 #endif
00969         current_agent(FIRING_TYPE) = IE_PRODS;
00970         current_agent(current_phase) = PREFERENCE_PHASE;
00971         break;
00972 
00973     case NEW_DECISION:
00974 #ifdef DEBUG_DETERMINE_LEVEL_PHASE
00975         print("\nThis is a new decision....");
00976 #endif
00977         current_agent(FIRING_TYPE) = IE_PRODS;
00978         current_agent(current_phase) = PREFERENCE_PHASE;
00979         break;
00980 
00981     case LOWER_LEVEL:
00982 #ifdef DEBUG_DETERMINE_LEVEL_PHASE
00983         print("\nThe level is lower than the previous level....");
00984 #endif
00985         /* There is always a minor quiescence at the previous level
00986            in the propose phase, so check for consistency. */
00987         if (!goal_stack_consistent_through_goal(current_agent(previous_active_goal))) {
00988             current_agent(current_phase) = DECISION_PHASE;
00989             break;
00990         }
00991 
00992         /* else: just do a preference phase */
00993         current_agent(FIRING_TYPE) = IE_PRODS;
00994         current_agent(current_phase) = PREFERENCE_PHASE;
00995         break;
00996 
00997     case SAME_LEVEL:
00998 #ifdef DEBUG_DETERMINE_LEVEL_PHASE
00999         print("\nThe level is the same as the previous level....");
01000 #endif
01001         current_agent(FIRING_TYPE) = IE_PRODS;
01002         current_agent(current_phase) = PREFERENCE_PHASE;
01003         break;
01004 
01005     case HIGHER_LEVEL:
01006 #ifdef DEBUG_DETERMINE_LEVEL_PHASE
01007         print("\nThe level is higher than the previous level....");
01008 #endif
01009 
01010         goal = current_agent(previous_active_goal);
01011         goal->id.saved_firing_type = current_agent(FIRING_TYPE);
01012 
01013 #ifdef DEBUG_DETERMINE_LEVEL_PHASE
01014         if (goal->id.saved_firing_type == IE_PRODS)
01015             print("\n Saving current firing type as IE_PRODS");
01016         else if (goal->id.saved_firing_type == PE_PRODS)
01017             print("\n Saving current firing type as PE_PRODS");
01018         else if (goal->id.saved_firing_type == NO_SAVED_PRODS)
01019             print("\n Saving current firing type as NO_SAVED_PRODS");
01020         else
01021             print("\n Unknown SAVED firing type???????");
01022 #endif
01023 
01024         /* run consistency check at new active level *before* firing any
01025            productions there */
01026 
01027 #ifdef DEBUG_DETERMINE_LEVEL_PHASE
01028         print("\nMinor quiescence at level %d", current_agent(active_level));
01029 #endif
01030         if (!goal_stack_consistent_through_goal(current_agent(active_goal))) {
01031             current_agent(current_phase) = DECISION_PHASE;
01032             break;
01033         }
01034 
01035         /* If the decision is consistent, then just start processing
01036            at this level */
01037 
01038         current_agent(FIRING_TYPE) = IE_PRODS;
01039         current_agent(current_phase) = PREFERENCE_PHASE;
01040         break;
01041     }
01042 
01043 }
01044 
01045 /* KJC: end   10.04.98 */

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