Soar Kernel  9.3.2 08-06-12
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
reorder.cpp
Go to the documentation of this file.
1 #include <portability.h>
2 
3 /*************************************************************************
4  * PLEASE SEE THE FILE "license.txt" (INCLUDED WITH THIS SOFTWARE PACKAGE)
5  * FOR LICENSE AND COPYRIGHT INFORMATION.
6  *************************************************************************/
7 
8 /*************************************************************************
9  *
10  * file: reorder.cpp
11  *
12  * =======================================================================
13  *
14  * Need header/description comments here
15  *
16  *
17  * =======================================================================
18  */
19 
20 #include <stdlib.h>
21 
22 #include "reorder.h"
23 #include "kernel.h"
24 #include "rhsfun.h"
25 #include "gdatastructs.h"
26 #include "production.h"
27 #include "mem.h"
28 #include "symtab.h"
29 #include "print.h"
30 #include "agent.h"
31 #include "init_soar.h"
32 #include "xml.h"
33 #include "soar_TraceNames.h"
34 
35 /* *********************************************************************
36 
37  Reordering
38 
39 ********************************************************************* */
40 
41 /* =====================================================================
42 
43  Name of production being reordered
44 
45  In case any errors are encountered during reordering, this variable
46  holds the name of the production currently being reordered, so it
47  can be printed with the error message.
48 ===================================================================== */
49 
50 /*#define symbol_is_constant_or_marked_variable(sym,tc) \
51  ( ((sym)->common.symbol_type!=VARIABLE_SYMBOL_TYPE) || \
52  ((sym)->var.tc_num == (tc)) )*/
54 {
55  return ( ((sym)->common.symbol_type!=VARIABLE_SYMBOL_TYPE) ||
56  ((sym)->var.tc_num == (tc)) );
57 }
58 
59 /* =====================================================================
60 
61  Reordering for RHS Actions
62 
63  Whenever a new identifier is created, we need to know (at creation time)
64  what level of the goal stack it's at. If the <newid> appears in the
65  attribute or value slot of a make, we just give it the same level as
66  whatever's in the id slot of that make. But if <newid> appears in the
67  id slot of a make, we can't tell what level it goes at.
68 
69  To avoid this problem, we reorder the list of RHS actions so that when
70  the actions are executed (in the resulting order), each <newid> is
71  encountered in an attribute or value slot *before* it is encountered
72  in an id slot.
73 
74  Furthermore, we make sure all arguments to function calls are bound
75  before the function call is executed.
76 
77  Reorder_action_list() does the reordering. Its parameter action_list
78  is reordered in place (destructively modified). It also requires at entry
79  that the variables bound on the LHS are marked. The function returns
80  TRUE if successful, FALSE if it was unable to produce a legal ordering.
81 ===================================================================== */
82 
84 
85 Bool reorder_action_list (agent* thisAgent, action **action_list,
86  tc_number lhs_tc) {
87  list *new_bound_vars;
88  action *remaining_actions;
89  action *first_action, *last_action;
90  action *a, *prev_a;
91  Bool result_flag;
92 
93  new_bound_vars = NIL;
94  remaining_actions = *action_list;
95  first_action = NIL;
96  last_action = NIL;
97 
98  while (remaining_actions) {
99  /* --- scan through remaining_actions, look for one that's legal --- */
100  prev_a = NIL;
101  a = remaining_actions;
102  while (TRUE) {
103  if (!a) break; /* looked at all candidates, but none were legal */
104  if (legal_to_execute_action (a, lhs_tc)) break;
105  prev_a = a;
106  a = a->next;
107  }
108  if (!a) break;
109  /* --- move action a from remaining_actions to reordered list --- */
110  if (prev_a) prev_a->next = a->next; else remaining_actions = a->next;
111  a->next = NIL;
112  if (last_action) last_action->next = a; else first_action = a;
113  last_action = a;
114  /* --- add new variables from a to new_bound_vars --- */
115  add_all_variables_in_action (thisAgent, a, lhs_tc, &new_bound_vars);
116  }
117 
118  if (remaining_actions) {
119  /* --- there are remaining_actions but none can be legally added --- */
120  print (thisAgent, "Error: production %s has a bad RHS--\n",
122  print (thisAgent, " Either it creates structure not connected to anything\n");
123  print (thisAgent, " else in WM, or it tries to pass an unbound variable as\n");
124  print (thisAgent, " an argument to a function.\n");
125  /* --- reconstruct list of all actions --- */
126  if (last_action)
127  last_action->next = remaining_actions;
128  else
129  first_action = remaining_actions;
130  result_flag = FALSE;
131  } else {
132  result_flag = TRUE;
133  }
134 
135  /* --- unmark variables that we just marked --- */
136  unmark_variables_and_free_list (thisAgent, new_bound_vars);
137 
138  /* --- return final result --- */
139  *action_list = first_action;
140  return result_flag;
141 }
142 
144  cons *c;
145  list *fl;
146  Symbol *sym;
147 
148  if (rhs_value_is_funcall(rv)) {
149  /* --- function calls --- */
150  fl = rhs_value_to_funcall_list (rv);
151  for (c=fl->rest; c!=NIL; c=c->rest)
152  if (! all_variables_in_rhs_value_bound (static_cast<char *>(c->first), tc))
153  return FALSE;
154  return TRUE;
155  } else {
156  /* --- ordinary (symbol) rhs values --- */
157  sym = rhs_value_to_symbol (rv);
158  if (sym->common.symbol_type==VARIABLE_SYMBOL_TYPE)
159  return (sym->var.tc_num == tc);
160  return TRUE;
161  }
162 }
163 
165  if (a->type==MAKE_ACTION) {
166  if (! all_variables_in_rhs_value_bound (a->id, tc)) return FALSE;
167  if (rhs_value_is_funcall(a->attr) &&
168  (! all_variables_in_rhs_value_bound (a->attr, tc))) return FALSE;
169  if (rhs_value_is_funcall(a->value) &&
170  (! all_variables_in_rhs_value_bound (a->value, tc))) return FALSE;
173  (! all_variables_in_rhs_value_bound (a->referent, tc))) return FALSE;
174  return TRUE;
175  }
176  /* --- otherwise it's a function call; make sure args are all bound --- */
177  return all_variables_in_rhs_value_bound (a->value, tc);
178 }
179 
180 /* =====================================================================
181 
182  Condition Simplification / Restoration
183 
184  In order to be able to move tests from one condition to another, we
185  reorder using the following high-level technique. (This procedure is
186  applied separately at each level of nesting.)
187 
188  1. Simplify the positive conditions, by stripping off all tests other
189  than equality. When this is done, all tests in positive conditions
190  are either equality tests or conjunctions of equality tests. All
191  other tests are saved away for later restoration.
192  2. Then do the reordering...
193  3. Then go back and restore all the tests that were previously saved
194  away. The restored tests might end up on different conditions
195  than they started--they're inserted in the first place possible
196  (i.e., as soon as all the necessary things are bound).
197 
198  The two main routines here are simplify_condition_list() and
199  restore_and_deallocate_saved_tests().
200 
201 ===================================================================== */
202 
203 typedef struct saved_test_struct {
207 } saved_test;
208 
209 
210 void print_saved_test (agent* thisAgent, saved_test *st) {
211  print_with_symbols (thisAgent, " Symbol: %y Test: ", st->var);
212  print_string (thisAgent, test_to_string (thisAgent, st->the_test, NULL, 0));
213 }
214 
215 void print_saved_test_list (agent* thisAgent, saved_test *st) {
216  while (st) {
217  print_saved_test (thisAgent, st);
218  print (thisAgent, "\n");
219  st = st->next;
220  }
221 }
222 
223 saved_test *simplify_test (agent* thisAgent, test *t, saved_test *old_sts) {
224  test New, subtest;
225  saved_test *saved;
226  Symbol *var, *sym;
227  cons *c, *prev_c, *next_c;
228  complex_test *ct;
229 
230  if (test_is_blank_test(*t)) {
231  sym = generate_new_variable (thisAgent, "dummy-");
233  return old_sts;
234  }
235 
237  return old_sts;
238  }
239 
240  ct = complex_test_from_test(*t);
241 
242  switch (ct->type) {
243 
244  case CONJUNCTIVE_TEST:
245  /* --- look at subtests for an equality test --- */
246  sym = NIL;
247  for (c=ct->data.conjunct_list; c!=NIL; c=c->rest) {
248  subtest = static_cast<char *>(c->first);
249  if (test_is_blank_or_equality_test(subtest))
250  sym = referent_of_equality_test(subtest);
251  }
252  /* --- if no equality test was found, generate a variable for it --- */
253  if (!sym) {
254  sym = generate_new_variable (thisAgent, "dummy-");
256  allocate_cons (thisAgent, &c);
257  c->first = New;
258  c->rest = ct->data.conjunct_list;
259  ct->data.conjunct_list = c;
260  }
261  /* --- scan through, create saved_test for subtests except equality --- */
262  prev_c = NIL;
263  c = ct->data.conjunct_list;
264  while (c) {
265  next_c = c->rest;
266  subtest = static_cast<char *>(c->first);
267  if (! test_is_blank_or_equality_test(subtest)) {
268  /* --- create saved_test, splice this cons out of conjunct_list --- */
269  allocate_with_pool (thisAgent, &thisAgent->saved_test_pool, &saved);
270  saved->next = old_sts;
271  old_sts = saved;
272  saved->var = sym;
273  symbol_add_ref (sym);
274  saved->the_test = subtest;
275  if (prev_c)
276  prev_c->rest = next_c;
277  else
278  ct->data.conjunct_list = next_c;
279  free_cons (thisAgent, c);
280  } else {
281  prev_c = c;
282  }
283  c = next_c;
284  }
285  break;
286 
287  default:
288  /* --- goal/impasse, disjunction, and non-equality relational tests --- */
289  var = generate_new_variable (thisAgent, "dummy-");
291  allocate_with_pool (thisAgent, &thisAgent->saved_test_pool, &saved);
292  saved->next = old_sts;
293  old_sts = saved;
294  saved->var = var;
295  symbol_add_ref (var);
296  saved->the_test = *t;
297  *t = New;
298  break;
299  }
300  return old_sts;
301 }
302 
303 saved_test *simplify_condition_list (agent* thisAgent, condition *conds_list) {
304  condition *c;
305  saved_test *sts;
306 
307  sts = NIL;
308  for (c=conds_list; c!=NIL; c=c->next) {
309 //#define CONSIDER_NEGATIVE 1
310 #ifdef CONSIDER_NEGATIVE
312 #else
313  if (c->type==POSITIVE_CONDITION) {
314 #endif
315  sts = simplify_test (thisAgent, &(c->data.tests.id_test), sts);
316  sts = simplify_test (thisAgent, &(c->data.tests.attr_test), sts);
317  sts = simplify_test (thisAgent, &(c->data.tests.value_test), sts);
318  }
319  }
320  return sts;
321 }
322 
324  switch (type) {
325  case NOT_EQUAL_TEST: return NOT_EQUAL_TEST;
326  case LESS_TEST: return GREATER_TEST;
327  case GREATER_TEST: return LESS_TEST;
330  case SAME_TYPE_TEST: return SAME_TYPE_TEST;
331  default:
332  { char msg[BUFFER_MSG_SIZE];
333  strncpy (msg,
334  "Internal error: arg to reverse_direction_of_relational_test\n", BUFFER_MSG_SIZE);
335  msg[BUFFER_MSG_SIZE - 1] = 0; /* ensure null termination */
336  abort_with_fatal_error(thisAgent, msg);
337  }
338  }
339  return 0; /* unreachable, but without it, gcc -Wall warns here */
340 }
341 
343  test *t,
344  Bool is_id_field,
345  tc_number bound_vars_tc_number,
346  saved_test *tests_to_restore, bool neg) {
347  saved_test *st, *prev_st, *next_st;
348  Bool added_it;
349  Symbol *referent;
350  complex_test *ct;
351 
352  prev_st = NIL;
353  st = tests_to_restore;
354  while (st) {
355  next_st = st->next;
356  added_it = FALSE;
358  switch (ct->type) {
359  case GOAL_ID_TEST:
360  case IMPASSE_ID_TEST:
361  if (! is_id_field) break; /* goal/impasse tests only go in id fields */
362  /* ... otherwise fall through to the next case below ... */
363  case DISJUNCTION_TEST:
365  add_new_test_to_test_if_not_already_there (thisAgent, t, st->the_test, neg);
366  added_it = TRUE;
367  }
368  break;
369  default: /* --- st->test is a relational test other than equality --- */
370  referent = ct->data.referent;
373  bound_vars_tc_number) ||
374  (st->var == referent)) {
375  add_new_test_to_test_if_not_already_there (thisAgent, t, st->the_test, neg);
376  added_it = TRUE;
377  }
378  } else if (test_includes_equality_test_for_symbol (*t, referent)) {
380  bound_vars_tc_number) ||
381  (st->var == referent)) {
382  ct->type = reverse_direction_of_relational_test (thisAgent, ct->type);
383  ct->data.referent = st->var;
384  st->var = referent;
385  add_new_test_to_test_if_not_already_there (thisAgent, t, st->the_test, neg);
386  added_it = TRUE;
387  }
388  }
389  break;
390  } /* end of switch statement */
391  if (added_it) {
392  if (prev_st) prev_st->next = next_st; else tests_to_restore = next_st;
393  symbol_remove_ref (thisAgent, st->var);
394  free_with_pool (&thisAgent->saved_test_pool, st);
395  } else {
396  prev_st = st;
397  }
398  st = next_st;
399  } /* end of while (st) */
400  return tests_to_restore;
401 }
402 
404  condition *conds_list,
405  /* tc number for vars bound outside */
406  tc_number tc,
407  saved_test *tests_to_restore) {
408  condition *cond;
409  list *new_vars;
410 
411  new_vars = NIL;
412  for (cond=conds_list; cond!=NIL; cond=cond->next) {
413 #ifdef CONSIDER_NEGATIVE
414  if (cond->type==CONJUNCTIVE_NEGATION_CONDITION) continue;
415 #else
416  if (cond->type!=POSITIVE_CONDITION) continue;
417 #endif
418  bool neg = cond->type == NEGATIVE_CONDITION;
419  tests_to_restore = restore_saved_tests_to_test
420  (thisAgent, (&cond->data.tests.id_test), TRUE, tc, tests_to_restore, neg);
421  add_bound_variables_in_test (thisAgent, cond->data.tests.id_test, tc, &new_vars);
422  tests_to_restore = restore_saved_tests_to_test
423  (thisAgent, (&cond->data.tests.attr_test), FALSE, tc, tests_to_restore, neg);
424  add_bound_variables_in_test (thisAgent, cond->data.tests.attr_test, tc, &new_vars);
425  tests_to_restore = restore_saved_tests_to_test
426  (thisAgent, (&cond->data.tests.value_test), FALSE, tc, tests_to_restore, neg);
427  add_bound_variables_in_test (thisAgent, cond->data.tests.value_test, tc, &new_vars);
428  }
429  if (tests_to_restore) {
430  if (thisAgent->sysparams[PRINT_WARNINGS_SYSPARAM]) {
431  print (thisAgent, "\nWarning: in production %s,\n",
433  print (thisAgent, " ignoring test(s) whose referent is unbound:\n");
434  print_saved_test_list (thisAgent, tests_to_restore);
435  // TODO: XML tagged output -- how to create this string?
436  // KJC TODO: need a tagged output version of print_saved_test_list
437 
438  // XML generation
440  add_to_growable_string(thisAgent, &gs, "Warning: in production ");
442  add_to_growable_string(thisAgent, &gs, "\n ignoring test(s) whose referent is unbound:");
443  //TODO: fill in XML print_saved_test_list. Possibile methods include:
444  // 1) write a version which adds to a growable string
445  // 2) write a version which generates XML tags/attributes, so we get "typed" output for this warning
446  // i.e. "<warning><string value="beginning of message"></string><test att="val"></test><string value="rest of message"></string></warning>
448 
449  free_growable_string(thisAgent, gs);
450  }
451  /* ought to deallocate the saved tests, but who cares */
452  }
453  unmark_variables_and_free_list (thisAgent, new_vars);
454 }
455 
456 /* =====================================================================
457 
458  Finding The Variables in a Negated Condition (or NCC)
459  That Refer to Variables Bound Outside
460 
461  If a variable occurs within a negated condition (or NCC), and that
462  same variable is bound by some positive condition outside the negation,
463  then the reorderer must ensure that the positive (binding) condition
464  comes before the negated (testing) condition. To do this, we put
465  a list on every NC or NCC of all the variables whose bindings it requires.
466 
467  When the reorderer is finished, these lists are removed.
468 
469  The main routines here are fill_in_vars_requiring_bindings() and
470  remove_vars_requiring_bindings(). Each of these recursively traverses
471  the lhs and does its work at all nesting levels.
472  Fill_in_vars_requiring_bindings() takes a tc_number parameter which
473  indicates the variables that are bound outside the given condition list.
474  (At the top level, this should be *no* variables.)
475 
476 ===================================================================== */
477 
479  tc_number tc,
480  list *starting_list) {
481  cons *c;
482  complex_test *ct;
483  Symbol *referent;
484 
485  if (test_is_blank_test(t)) return starting_list;
486 
488  referent = referent_of_equality_test(t);
489  if (referent->common.symbol_type==VARIABLE_SYMBOL_TYPE)
490  if (referent->var.tc_num == tc)
491  starting_list = add_if_not_member (thisAgent, referent, starting_list);
492  return starting_list;
493  }
494 
495  ct = complex_test_from_test(t);
496 
497  switch (ct->type) {
498  case GOAL_ID_TEST:
499  case IMPASSE_ID_TEST:
500  case DISJUNCTION_TEST:
501  return starting_list;
502 
503  case CONJUNCTIVE_TEST:
504  for (c=ct->data.conjunct_list; c!=NIL; c=c->rest)
506  (thisAgent, static_cast<char *>(c->first), tc, starting_list);
507  return starting_list;
508 
509  default:
510  /* --- relational tests other than equality --- */
511  referent = ct->data.referent;
512  if (referent->common.symbol_type==VARIABLE_SYMBOL_TYPE)
513  if (referent->var.tc_num == tc)
514  starting_list = add_if_not_member (thisAgent, referent, starting_list);
515  return starting_list;
516  }
517 }
518 
520  condition *cond,
521  tc_number tc,
522  list *starting_list) {
523  condition *c;
524 
526  /* --- conjuctive negations --- */
527  for (c=cond->data.ncc.top; c!=NIL; c=c->next)
529  (thisAgent, c, tc, starting_list);
530  } else {
531  /* --- positive, negative conditions --- */
533  (thisAgent, cond->data.tests.id_test, tc, starting_list);
535  (thisAgent, cond->data.tests.attr_test, tc, starting_list);
537  (thisAgent, cond->data.tests.value_test, tc, starting_list);
538  }
539  return starting_list;
540 }
541 
542 void fill_in_vars_requiring_bindings (agent* thisAgent, condition *cond_list, tc_number tc) {
543  list *new_bound_vars;
544  condition *c;
545 
546  /* --- add anything bound in a positive condition at this level --- */
547  new_bound_vars = NIL;
548  for (c=cond_list; c!=NIL; c=c->next)
549  if (c->type==POSITIVE_CONDITION)
550  add_bound_variables_in_condition (thisAgent, c, tc, &new_bound_vars);
551 
552  /* --- scan through negated and NC cond's, fill in stuff --- */
553  for (c=cond_list; c!=NIL; c=c->next) {
554  if (c->type!=POSITIVE_CONDITION)
558  fill_in_vars_requiring_bindings (thisAgent, c->data.ncc.top, tc);
559  }
560 
561  unmark_variables_and_free_list (thisAgent, new_bound_vars);
562 }
563 
565  condition *cond_list) {
566  condition *c;
567 
568  /* --- scan through negated and NC cond's, remove lists from them --- */
569  for (c=cond_list; c!=NIL; c=c->next) {
570  if (c->type!=POSITIVE_CONDITION)
571  free_list (thisAgent, c->reorder.vars_requiring_bindings);
573  remove_vars_requiring_bindings (thisAgent, c->data.ncc.top);
574  }
575 }
576 
577 /* =====================================================================
578 
579  Finding the Root Variables in a Condition List
580 
581  This routine finds the root variables in a given condition list.
582  The caller should setup the current tc to be the set of variables
583  bound outside the given condition list. (This should normally be
584  an empty TC, except when the condition list is the subconditions
585  of an NCC.) If the "allow_printing_warnings" flag is TRUE, then
586  this routine makes sure each root variable is accompanied by a
587  goal or impasse id test, and prints a warning message if it isn't.
588 ===================================================================== */
589 
591  condition *cond_list,
592  tc_number tc, /* for vars bound outside */
593  Bool allow_printing_warnings) {
594 
595  list *new_vars_from_value_slot;
596  list *new_vars_from_id_slot;
597  cons *c;
598  condition *cond;
599  Bool found_goal_impasse_test;
600 
601  /* --- find everthing that's in the value slot of some condition --- */
602  new_vars_from_value_slot = NIL;
603  for (cond=cond_list; cond!=NIL; cond=cond->next)
604  if (cond->type==POSITIVE_CONDITION)
605  add_bound_variables_in_test (thisAgent, cond->data.tests.value_test, tc,
606  &new_vars_from_value_slot);
607 
608  /* --- now see what else we can add by throwing in the id slot --- */
609  new_vars_from_id_slot = NIL;
610  for (cond=cond_list; cond!=NIL; cond=cond->next)
611  if (cond->type==POSITIVE_CONDITION)
612  add_bound_variables_in_test (thisAgent, cond->data.tests.id_test, tc,
613  &new_vars_from_id_slot);
614 
615  /* --- unmark everything we just marked --- */
616  unmark_variables_and_free_list (thisAgent, new_vars_from_value_slot);
617  for (c=new_vars_from_id_slot; c!=NIL; c=c->rest)
618  static_cast<Symbol *>(c->first)->var.tc_num = 0;
619 
620  /* --- make sure each root var has some condition with goal/impasse --- */
621  if (allow_printing_warnings && thisAgent->sysparams[PRINT_WARNINGS_SYSPARAM]) {
622  for (c=new_vars_from_id_slot; c!=NIL; c=c->rest) {
623  found_goal_impasse_test = FALSE;
624  for (cond=cond_list; cond!=NIL; cond=cond->next) {
625  if (cond->type!=POSITIVE_CONDITION) continue;
627  static_cast<symbol_union *>(c->first)))
629  TRUE, TRUE)) {
630  found_goal_impasse_test = TRUE;
631  break;
632  }
633  }
634  if (! found_goal_impasse_test) {
635  print (thisAgent, "\nWarning: On the LHS of production %s, identifier ",
637  print_with_symbols (thisAgent, "%y is not connected to any goal or impasse.\n",
638  static_cast<Symbol *>(c->first));
639 
640  // XML geneneration
642  add_to_growable_string(thisAgent, &gs, "Warning: On the LHS of production ");
644  add_to_growable_string(thisAgent, &gs, ", identifier ");
645  add_to_growable_string(thisAgent, &gs, symbol_to_string (thisAgent, static_cast<Symbol *>(c->first), true, 0, 0));
646  add_to_growable_string(thisAgent, &gs, " is not connected to any goal or impasse.");
648  free_growable_string(thisAgent, gs);
649 
650  }
651  }
652  }
653 
654  return new_vars_from_id_slot;
655 }
656 
657 /* =====================================================================
658 
659  Reordering for LHS Conditions
660 
661  (Sorry for the poor comments here. I think the reorderer needs
662  substantial revisions in order to make Soar reliably scalable, so
663  most of this code will eventually get thrown out anyway...)
664 ===================================================================== */
665 
666 /* --- estimated k-search branching factors --- */
667 #define MAX_COST 10000005 /* cost of a disconnected condition */
668 #define BF_FOR_ACCEPTABLE_PREFS 8 /* cost of (. ^. <var> +) */
669 #define BF_FOR_VALUES 8 /* cost of (. ^. <var>) */
670 #define BF_FOR_ATTRIBUTES 8 /* cost of (. ^<var> .) */
671 
672 /* -------------------------------------------------------------
673  Return TRUE iff the given test is covered by the previously
674  bound variables. The set of previously bound variables is
675  given by the current TC, PLUS any variables in the list
676  "extra_vars."
677 ------------------------------------------------------------- */
678 
680  cons *c;
681  Symbol *referent;
682  complex_test *ct;
683 
684  if (test_is_blank_test(t)) return FALSE;
685 
687  referent = referent_of_equality_test(t);
688  if (symbol_is_constant_or_marked_variable (referent, tc))
689  return TRUE;
690  if (extra_vars) return member_of_list (referent, extra_vars);
691  return FALSE;
692  }
693 
694  ct = complex_test_from_test(t);
695  if (ct->type==CONJUNCTIVE_TEST) {
696  for (c=ct->data.conjunct_list; c!=NIL; c=c->rest)
697  if (test_covered_by_bound_vars (static_cast<char *>(c->first), tc, extra_vars))
698  return TRUE;
699  }
700  return FALSE;
701 }
702 
703 /* -------------------------------------------------------------
704  Returns the user set value of the expected match cost of the
705  multi-attribute, or 1 if the input symbol isn't in the user
706  set list.
707 ------------------------------------------------------------- */
708 
710 {
711  multi_attribute *m = thisAgent->multi_attributes;
712  while(m) {
713  if(m->symbol == sym) return m->value;
714  m = m->next;
715  }
716  return 1;
717 }
718 
719 /* -------------------------------------------------------------
720  Return an estimate of the "cost" of the given condition.
721  The current TC should be the set of previously bound variables;
722  "root_vars_not_bound_yet" should be the set of other root
723  variables.
724 ------------------------------------------------------------- */
725 
726 int64_t cost_of_adding_condition (agent* thisAgent,
727  condition *cond,
728  tc_number tc,
729  list *root_vars_not_bound_yet) {
730  cons *c;
731  int64_t result;
732 
733  /* --- handle the common simple case quickly up front --- */
734  if ((! root_vars_not_bound_yet) &&
735  (cond->type==POSITIVE_CONDITION) &&
739  (! test_is_blank_test (cond->data.tests.id_test)) &&
740  (! test_is_blank_test (cond->data.tests.attr_test)) &&
741  (! test_is_blank_test (cond->data.tests.value_test)) ) {
742 
745  return MAX_COST;
749  (thisAgent, referent_of_equality_test (cond->data.tests.attr_test));
750  else
751  result = BF_FOR_ATTRIBUTES;
752 
756  result = result * BF_FOR_ACCEPTABLE_PREFS;
757  else
758  result = result * BF_FOR_VALUES;
759  }
760  return result;
761  } /* --- end of common simple case --- */
762 
763  if (cond->type==POSITIVE_CONDITION) {
764  /* --- for pos cond's, check what's bound, etc. --- */
765  if (! test_covered_by_bound_vars (cond->data.tests.id_test, tc,
766  root_vars_not_bound_yet))
767  return MAX_COST;
769  root_vars_not_bound_yet))
770  result = 1;
771  else
772  result = BF_FOR_ATTRIBUTES;
774  root_vars_not_bound_yet)) {
776  result = result * BF_FOR_ACCEPTABLE_PREFS;
777  else
778  result = result * BF_FOR_VALUES;
779  }
780  return result;
781  }
782  /* --- negated or NC conditions: just check whether all variables
783  requiring bindings are actually bound. If so, return 1, else
784  return MAX_COST --- */
785  for (c=cond->reorder.vars_requiring_bindings; c!=NIL; c=c->rest) {
786  if (static_cast<Symbol *>(c->first)->var.tc_num != tc) return MAX_COST;
787  }
788  return 1;
789 }
790 
791 /* -------------------------------------------------------------
792  Return an estimate of the "cost" of the lowest-cost condition
793  that could be added next, IF the given "chosen" condition is
794  added first.
795 ------------------------------------------------------------- */
796 
797 int64_t find_lowest_cost_lookahead (agent* thisAgent,
798  condition *candidates,
799  condition *chosen,
800  tc_number tc,
801  list *root_vars_not_bound_yet) {
802  condition *c;
803  int64_t min_cost, cost;
804  list *new_vars;
805 
806  new_vars = NIL;
807  add_bound_variables_in_condition (thisAgent, chosen, tc, &new_vars);
808  min_cost = MAX_COST + 1;
809  for (c=candidates; c!=NIL; c=c->next) {
810  if (c==chosen) continue;
811  cost = cost_of_adding_condition (thisAgent, c, tc, root_vars_not_bound_yet);
812  if (cost < min_cost) {
813  min_cost = cost;
814  if (cost <= 1) break;
815  }
816  }
817  unmark_variables_and_free_list (thisAgent, new_vars);
818  return min_cost;
819 }
820 
821 /* -------------------------------------------------------------
822  Reorder the given list of conditions. The "top_of_conds" and
823  "bottom_of_conds" arguments are destructively modified to reflect
824  the reordered conditions. The "bound_vars_tc_number"
825  should reflect the variables bound outside the given condition list.
826  The "reorder_nccs" flag indicates whether it is necessary to
827  recursively reorder the subconditions of NCC's. (For newly
828  built chunks, this is never necessary.)
829 ------------------------------------------------------------- */
830 
831 void reorder_condition_list (agent* thisAgent,
832  condition **top_of_conds,
833  condition **bottom_of_conds,
834  list *roots,
835  tc_number tc,
836  Bool reorder_nccs);
837 
839  condition **top_of_conds,
840  condition **bottom_of_conds,
841  list *roots,
842  tc_number bound_vars_tc_number,
843  Bool reorder_nccs) {
844  condition *remaining_conds; /* header of dll */
845  condition *first_cond, *last_cond;
846  condition *cond, *next_cond;
847  condition *min_cost_conds, *chosen;
848  int64_t cost = 0;
849  int64_t min_cost = 0;
850  list *new_vars;
851 
852  remaining_conds = *top_of_conds;
853  first_cond = NIL;
854  last_cond = NIL;
855  new_vars = NIL;
856 
857  /* repeat: scan through remaining_conds
858  rate each one
859  if tie, call lookahead routine
860  add min-cost item to conds
861  */
862 
863  while (remaining_conds) {
864  /* --- find min-cost set --- */
865  min_cost_conds = NIL;
866  min_cost = 0;
867  for (cond=remaining_conds; cond!=NIL; cond=cond->next) {
868  cost = cost_of_adding_condition (thisAgent, cond, bound_vars_tc_number, roots);
869  if ((! min_cost_conds) || (cost < min_cost)) {
870  min_cost = cost;
871  min_cost_conds = cond;
872  cond->reorder.next_min_cost = NIL;
873  } else if (cost==min_cost) {
874  cond->reorder.next_min_cost = min_cost_conds;
875  min_cost_conds = cond;
876  }
877  /* if (min_cost <= 1) break; This optimization needs to be removed,
878  otherwise the tie set is not created.
879  Without the tie set we can't check the
880  canonical order. */
881  }
882  /* --- if min_cost==MAX_COST, print error message --- */
883  if ((min_cost==MAX_COST) &&
884  thisAgent->sysparams[PRINT_WARNINGS_SYSPARAM]) {
885  print (thisAgent, "Warning: in production %s,\n",
887  print (thisAgent, " The LHS conditions are not all connected.\n");
888  /* BUGBUG I'm not sure whether this can ever happen. */
889 
890  // XML geneneration
892  add_to_growable_string(thisAgent, &gs, "Warning: in production ");
894  add_to_growable_string(thisAgent, &gs, "\n The LHS conditions are not all connected.");
896  free_growable_string(thisAgent, gs);
897 
898  }
899  /* --- if more than one min-cost item, and cost>1, do lookahead --- */
900  if ((min_cost > 1) && (min_cost_conds->reorder.next_min_cost)) {
901  min_cost = MAX_COST + 1;
902  for (cond=min_cost_conds, next_cond = cond->reorder.next_min_cost;
903  cond!=NIL;
904  cond=next_cond, next_cond=(cond?cond->reorder.next_min_cost:NIL)) {
905  cost = find_lowest_cost_lookahead (thisAgent, remaining_conds, cond,
906  bound_vars_tc_number, roots);
907  if (cost < min_cost) {
908  min_cost = cost;
909  min_cost_conds = cond;
910  cond->reorder.next_min_cost = NIL;
911  } else {
912 /*******************************************************************
913  These code segments find the condition in the tie set with the smallest
914  value in the canonical order. This ensures that productions with the
915  same set of conditions are ordered the same. Except if the variables
916  are assigned differently.
917 *********************************************************************/
918  if (cost == min_cost && cond->type == POSITIVE_CONDITION) {
919  if (canonical_cond_greater(min_cost_conds,cond)) {
920  min_cost = cost;
921  min_cost_conds = cond;
922  cond->reorder.next_min_cost = NIL;
923  }
924  }
925  }
926 /*******************************************************************/
927 
928  }
929  }
930 /*******************************************************************/
931  if (min_cost == 1 && (min_cost_conds->reorder.next_min_cost)) {
932  for (cond=min_cost_conds; cond!=NIL; cond=cond->reorder.next_min_cost) {
933  if (cond->type == POSITIVE_CONDITION &&
934  min_cost_conds->type == POSITIVE_CONDITION &&
935  canonical_cond_greater(min_cost_conds,cond))
936  {
937  min_cost = cost;
938  min_cost_conds = cond;
939  }
940  else if (cond->type != POSITIVE_CONDITION &&
941  min_cost_conds->type == POSITIVE_CONDITION)
942  {
943  min_cost = cost;
944  min_cost_conds = cond;
945  }
946  }
947  }
948 /*******************************************************************/
949 
950  /* --- install the first item in the min-cost set --- */
951  chosen = min_cost_conds;
952  remove_from_dll (remaining_conds, chosen, next, prev);
953  if (!first_cond) first_cond = chosen;
954  /* Note: args look weird on the next line, because we're really
955  inserting the chosen item at the *end* of the list */
956  insert_at_head_of_dll (last_cond, chosen, prev, next);
957 
958  /* --- if a conjunctive negation, recursively reorder its conditions --- */
959  if ((chosen->type==CONJUNCTIVE_NEGATION_CONDITION) && reorder_nccs) {
960  list *ncc_roots;
961  ncc_roots = collect_root_variables (thisAgent, chosen->data.ncc.top,
962  bound_vars_tc_number, TRUE);
963  reorder_condition_list (thisAgent, &(chosen->data.ncc.top),
964  &(chosen->data.ncc.bottom),
965  ncc_roots,
966  bound_vars_tc_number,
967  reorder_nccs);
968  free_list (thisAgent, ncc_roots);
969  }
970 
971  /* --- update set of bound variables for newly added condition --- */
972  add_bound_variables_in_condition (thisAgent, chosen, bound_vars_tc_number, &new_vars);
973 
974  /* --- if all roots are bound, set roots=NIL: don't need 'em anymore --- */
975  if (roots) {
976  cons *c;
977  for (c=roots; c!=NIL; c=c->rest)
978  if (static_cast<Symbol *>(c->first)->var.tc_num != bound_vars_tc_number)
979  break;
980  if (!c) roots=NIL;
981  }
982 
983  } /* end of while (remaining_conds) */
984 
985  unmark_variables_and_free_list (thisAgent, new_vars);
986  *top_of_conds = first_cond;
987  *bottom_of_conds = last_cond;
988 }
989 
990 void reorder_condition_list (agent* thisAgent,
991  condition **top_of_conds,
992  condition **bottom_of_conds,
993  list *roots,
994  tc_number tc, /* for vars bound outside */
995  Bool reorder_nccs) {
996  saved_test *saved_tests;
997 
998  saved_tests = simplify_condition_list (thisAgent, *top_of_conds);
999  reorder_simplified_conditions (thisAgent, top_of_conds, bottom_of_conds, roots, tc,
1000  reorder_nccs);
1001  restore_and_deallocate_saved_tests (thisAgent, *top_of_conds, tc, saved_tests);
1002 }
1003 
1004 /* -------------------------------------------------------------
1005  Reorders the LHS.
1006 ------------------------------------------------------------- */
1007 
1008 /* SBH/MVP 6-24-94 */
1009 
1011 
1012  cons *c;
1013  complex_test *ct;
1014  Symbol *referent;
1015 
1016  /* Gather variables from test. */
1017 
1018  if (test_is_blank_test(t)) return FALSE;
1019 
1021  referent = referent_of_equality_test(t);
1022  if ((referent->common.symbol_type==VARIABLE_SYMBOL_TYPE) &&
1023  member_of_list(referent,roots)) return TRUE;
1024  return FALSE;
1025  }
1026  ct = complex_test_from_test(t);
1027 
1028  switch (ct->type) {
1029  case GOAL_ID_TEST:
1030  case IMPASSE_ID_TEST:
1031  case DISJUNCTION_TEST:
1032  return FALSE;
1033  break;
1034 
1035  case CONJUNCTIVE_TEST:
1036  for (c=ct->data.conjunct_list; c!=NIL; c=c->rest)
1037  if (test_tests_for_root(static_cast<char *>(c->first), roots)) return TRUE;
1038  return FALSE;
1039  break;
1040 
1041  default:
1042  /* --- relational tests other than equality --- */
1043  referent = ct->data.referent;
1044  if ((referent->common.symbol_type==VARIABLE_SYMBOL_TYPE) &&
1045  member_of_list(referent,roots)) return TRUE;
1046  return FALSE;
1047  break;
1048  }
1049 }
1050 
1051 /* -------------------------------------------------------------
1052  check_unbound_negative_relational_test_referents
1053  check_negative_relational_test_bindings
1054 
1055  These two functions are for fixing bug 517. The bug stems
1056  from two different code paths being used to check the bound
1057  variables after reordering the left hand side; one for
1058  positive conditions and one for negated conditions.
1059 
1060  Specifically, the old system would let unbound referents of
1061  non-equality relational tests continue past the reordering
1062  until the production addition failed as the bad production
1063  was added to the rete.
1064 
1065  These two functions specifically check that all referents
1066  of non-equality relational tests are bound and return false
1067  if an unbound referent is discovered.
1068 
1069  There may be a faster way of checking for this inside of
1070  the existing calls to fill_in_vars_requiring_bindings and
1071  reorder_condition_list, but my last attempt at fixing it
1072  there failed.
1073 
1074  Example bad production:
1075  sp {test
1076  (state <s> ^superstate nil -^foo {<> <bar>})
1077  -->
1078  }
1079 ------------------------------------------------------------- */
1081 {
1082  cons *c;
1083  complex_test *ct;
1084  Symbol *referent;
1085 
1086  // we only care about relational tests other than equality
1087  if (test_is_blank_test(t)) return true;
1088  if (test_is_blank_or_equality_test(t)) return true;
1089 
1090  ct = complex_test_from_test(t);
1091  switch (ct->type) {
1092  case GOAL_ID_TEST:
1093  case IMPASSE_ID_TEST:
1094  case DISJUNCTION_TEST:
1095  break;
1096 
1097  case CONJUNCTIVE_TEST:
1098  // we do need to loop over conjunctive tests, however
1099  for (c=ct->data.conjunct_list; c!=NIL; c=c->rest)
1100  if (!check_unbound_negative_relational_test_referents (thisAgent, static_cast<test>(c->first), tc))
1101  return false;
1102  break;
1103 
1104  default:
1105  /* --- relational tests other than equality --- */
1106  referent = ct->data.referent;
1107  if (referent->common.symbol_type==VARIABLE_SYMBOL_TYPE) {
1108  if (referent->var.tc_num != tc) {
1109  print (thisAgent,
1110  "Error: production %s has an unbound referent in negated relational test %s",
1112  test_to_string (thisAgent, t, NULL, 0));
1113  return false;
1114  }
1115  }
1116  break;
1117  }
1118  return true;
1119 }
1120 
1122 {
1123  list* bound_vars = NIL; // this list necessary pop variables bound inside ncc's out of scope on return
1124  condition *c;
1125  bool ret = true;
1126 
1127  /* --- add anything bound in a positive condition at this level --- */
1128  /* --- recurse in to NCCs --- */
1129  for (c=cond_list; ret && c!=NIL; c=c->next) {
1130  if (c->type==POSITIVE_CONDITION)
1131  add_bound_variables_in_condition (thisAgent, c, tc, &bound_vars);
1132  else if (c->type==CONJUNCTIVE_NEGATION_CONDITION) {
1133  ret = check_negative_relational_test_bindings (thisAgent, c->data.ncc.top, tc);
1134  }
1135  }
1136 
1137  /* --- find referents of non-equality tests in conjunctive tests in negated conditions ---*/
1138  for (c=cond_list; ret && c!=NIL; c=c->next) {
1139  if (c->type==NEGATIVE_CONDITION) {
1141  ret = ret && check_unbound_negative_relational_test_referents (thisAgent, c->data.tests.attr_test, tc);
1142  ret = ret && check_unbound_negative_relational_test_referents (thisAgent, c->data.tests.value_test, tc);
1143  }
1144  }
1145 
1146  // unmark anything bound on this level
1147  unmark_variables_and_free_list (thisAgent, bound_vars);
1148  return ret;
1149 }
1150 
1152  condition ** /*lhs_bottom*/, list *roots)
1153 {
1154  condition *cond;
1155  Bool a,b;
1156  test temp;
1157 
1158  a = FALSE;
1159  b = FALSE;
1160 
1161  for (cond = *lhs_top; cond != NIL; cond = cond->next) {
1162  if ((cond->type == POSITIVE_CONDITION) &&
1165  TRUE, FALSE)) &&
1166  (!test_tests_for_root(cond->data.tests.id_test, roots))) {
1167  temp = cond->data.tests.id_test;
1168  cond->data.tests.id_test =
1169  copy_test_removing_goal_impasse_tests (thisAgent, temp,&a,&b);
1170  deallocate_test (thisAgent, temp); /* RBD fixed memory leak 3/29/95 */
1171  }
1172  }
1173 }
1174 
1175 Bool reorder_lhs (agent* thisAgent, condition **lhs_top,
1176  condition **lhs_bottom, Bool reorder_nccs) {
1177  tc_number tc;
1178  list *roots;
1179 
1180  tc = get_new_tc_number (thisAgent);
1181  /* don't mark any variables, since nothing is bound outside the LHS */
1182 
1183  roots = collect_root_variables (thisAgent, *lhs_top, tc, TRUE);
1184 
1185 
1186  /* SBH/MVP 6-24-94 Fix to include only root "STATE" test in the LHS of a chunk.*/
1187  if (roots) remove_isa_state_tests_for_non_roots(thisAgent, lhs_top,lhs_bottom,roots);
1188 
1189  /* MVP 6-8-94 - fix provided by Bob */
1190  if (!roots) {
1191  condition *cond;
1192 
1193  for (cond = *lhs_top; cond!=NIL; cond=cond->next) {
1194  if ((cond->type == POSITIVE_CONDITION) &&
1196  TRUE, FALSE))) {
1197  add_bound_variables_in_test (thisAgent, cond->data.tests.id_test, tc, &roots);
1198  if (roots) break;
1199  }
1200  }
1201  }
1202 
1203  if (!roots) {
1204  print (thisAgent, "Error: in production %s,\n", thisAgent->name_of_production_being_reordered);
1205  print (thisAgent, " The LHS has no roots.\n");
1206  /* hmmm... most people aren't going to understand this error message */
1207  return FALSE;
1208  }
1209 
1210  fill_in_vars_requiring_bindings (thisAgent, *lhs_top, tc);
1211  reorder_condition_list (thisAgent, lhs_top, lhs_bottom, roots, tc, reorder_nccs);
1212  remove_vars_requiring_bindings (thisAgent, *lhs_top);
1213  free_list (thisAgent, roots);
1214 
1215  return check_negative_relational_test_bindings (thisAgent, *lhs_top, get_new_tc_number (thisAgent));
1216 }
1217 
1218 void init_reorderer (agent* thisAgent) { /* called from init_production_utilities() */
1219  init_memory_pool (thisAgent, &thisAgent->saved_test_pool, sizeof(saved_test), "saved test");
1220 }
1221