Soar Kernel  9.3.2 08-06-12
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
chunk.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: chunk.cpp
11  *
12  * =======================================================================
13  * Supports the learning mechanism in Soar. Learning can be set
14  * on | off | only | except (for other choices see soarCommands.c: learn).
15  * If set to "only" | "except" users must specify rhs functions in
16  * productions: dont-learn | force-learn. See rhsfun.c
17  * =======================================================================
18  */
19 
20 
21 /* ====================================================================
22 
23  Chunking Routines
24 
25  ==================================================================== */
26 
27 #include <stdlib.h>
28 
29 #include "chunk.h"
30 #include "symtab.h"
31 #include "wmem.h"
32 #include "gdatastructs.h"
33 #include "kernel.h"
34 #include "agent.h"
35 #include "instantiations.h"
36 #include "production.h"
37 #include "rhsfun.h"
38 #include "print.h"
39 #include "init_soar.h"
40 #include "prefmem.h"
41 #include "decide.h"
42 #include "explain.h"
43 #include "backtrace.h"
44 #include "recmem.h"
45 #include "rete.h"
46 #include "xml.h"
47 #include "soar_TraceNames.h"
48 
49 #include "wma.h"
50 
51 #include <ctype.h>
52 
53 using namespace soar_TraceNames;
54 
55 /* =====================================================================
56 
57  Results Calculation
58 
59  Get_results_for_instantiation() finds and returns the result preferences
60  for a given instantiation. This is the main routine here.
61 
62  The results are accumulated in the list "results," linked via the
63  "next_result" field of the preference structures. (NOTE: to save
64  space, just use conses for this.)
65 
66  Add_pref_to_results() adds a preference to the results.
67  Add_results_for_id() adds any preferences for the given identifier.
68  Identifiers are marked with results_tc_number as they are added.
69 ===================================================================== */
70 
71 #ifdef USE_MACROS
72 #define add_results_if_needed(thisAgent, sym) \
73  { if ((sym)->common.symbol_type==IDENTIFIER_SYMBOL_TYPE) \
74  if ( ((sym)->id.level >= thisAgent->results_match_goal_level) && \
75  ((sym)->id.tc_num != thisAgent->results_tc_number) ) \
76  add_results_for_id(thisAgent, sym); }
77 
78 #else
79 inline void add_results_if_needed(agent* thisAgent, Symbol * sym)
80 {
81  if ((sym)->common.symbol_type==IDENTIFIER_SYMBOL_TYPE)
82  if ( ((sym)->id.level >= thisAgent->results_match_goal_level) &&
83  ((sym)->id.tc_num != thisAgent->results_tc_number) )
84  add_results_for_id(thisAgent, sym);
85 }
86 #endif /* USE_MACROS */
87 
88 extern void add_pref_to_results (agent* thisAgent, preference *pref) {
89  preference *p;
90 
91  /* --- if an equivalent pref is already a result, don't add this one --- */
92  for (p=thisAgent->results; p!=NIL; p=p->next_result) {
93  if (p->id!=pref->id) continue;
94  if (p->attr!=pref->attr) continue;
95  if (p->value!=pref->value) continue;
96  if (p->type!=pref->type) continue;
97  if (preference_is_unary(pref->type)) return;
98  if (p->referent!=pref->referent) continue;
99  return;
100  }
101 
102  /* --- if pref isn't at the right level, find a clone that is --- */
103  if (pref->inst->match_goal_level != thisAgent->results_match_goal_level) {
104  for (p=pref->next_clone; p!=NIL; p=p->next_clone)
105  if (p->inst->match_goal_level == thisAgent->results_match_goal_level) break;
106  if (!p)
107  for (p=pref->prev_clone; p!=NIL; p=p->prev_clone)
108  if (p->inst->match_goal_level == thisAgent->results_match_goal_level) break;
109  if (!p) return; /* if can't find one, it isn't a result */
110  pref = p;
111  }
112 
113  /* --- add this preference to the result list --- */
114  pref->next_result = thisAgent->results;
115  thisAgent->results = pref;
116 
117  /* --- follow transitive closuse through value, referent links --- */
118  add_results_if_needed (thisAgent, pref->value);
119  if (preference_is_binary(pref->type))
120  add_results_if_needed (thisAgent, pref->referent);
121 }
122 
123 void add_results_for_id (agent* thisAgent, Symbol *id) {
124  slot *s;
125  preference *pref;
126  wme *w;
127 
128  id->id.tc_num = thisAgent->results_tc_number;
129 
130  /* --- scan through all preferences and wmes for all slots for this id --- */
131  for (w=id->id.input_wmes; w!=NIL; w=w->next)
132  add_results_if_needed (thisAgent, w->value);
133  for (s=id->id.slots; s!=NIL; s=s->next) {
134  for (pref=s->all_preferences; pref!=NIL; pref=pref->all_of_slot_next)
135  add_pref_to_results(thisAgent, pref);
136  for (w=s->wmes; w!=NIL; w=w->next)
137  add_results_if_needed (thisAgent, w->value);
138  } /* end of for slots loop */
139  /* --- now scan through extra prefs and look for any with this id --- */
140  for (pref=thisAgent->extra_result_prefs_from_instantiation; pref!=NIL;
141  pref=pref->inst_next) {
142  if (pref->id==id) add_pref_to_results(thisAgent, pref);
143  }
144 }
145 
147  preference *pref;
148 
149  thisAgent->results = NIL;
150  thisAgent->results_match_goal_level = inst->match_goal_level;
151  thisAgent->results_tc_number = get_new_tc_number(thisAgent);
153  for (pref=inst->preferences_generated; pref!=NIL; pref=pref->inst_next)
154  if ( (pref->id->id.level < thisAgent->results_match_goal_level) &&
155  (pref->id->id.tc_num != thisAgent->results_tc_number) ) {
156  add_pref_to_results(thisAgent, pref);
157  }
158  return thisAgent->results;
159 }
160 
161 /* =====================================================================
162 
163  Variablizing Conditions and Results
164 
165  Variablizing of conditions is done by walking over a condition list
166  and destructively modifying it, replacing tests of identifiers with
167  tests of tests of variables. The identifier-to-variable mapping is
168  built as we go along: identifiers that have already been assigned
169  a variablization are marked with id.tc_num==variablization_tc, and
170  id.variablization points to the corresponding variable.
171 
172  Variablizing of results can't be done destructively because we need
173  to convert the results--preferences--into actions. This is done
174  by copy_and_variablize_result_list(), which takes the result preferences
175  and returns an action list.
176 ===================================================================== */
177 
178 /* sym is both an input and output parameter */
179 void variablize_symbol (agent* thisAgent, Symbol **sym) {
180  char prefix[2];
181  Symbol *var;
182 
183  if ((*sym)->common.symbol_type!=IDENTIFIER_SYMBOL_TYPE) return; // only variablize identifiers
184  if ((*sym)->id.smem_lti != NIL) // don't variablize lti (long term identifiers)
185  {
186  (*sym)->id.tc_num = thisAgent->variablization_tc;
187  (*sym)->id.variablization = (*sym);
188  return;
189  }
190 
191  if ((*sym)->id.tc_num == thisAgent->variablization_tc) {
192  /* --- it's already been variablized, so use the existing variable --- */
193  var = (*sym)->id.variablization;
194  symbol_remove_ref (thisAgent, *sym);
195  *sym = var;
196  symbol_add_ref (var);
197  return;
198  }
199 
200  /* --- need to create a new variable --- */
201  (*sym)->id.tc_num = thisAgent->variablization_tc;
202  prefix[0] = static_cast<char>(tolower((*sym)->id.name_letter));
203  prefix[1] = 0;
204  var = generate_new_variable (thisAgent, prefix);
205  (*sym)->id.variablization = var;
206  symbol_remove_ref (thisAgent, *sym);
207  *sym = var;
208 }
209 
210 void variablize_test (agent* thisAgent, test *t) {
211  cons *c;
212  complex_test *ct;
213 
214  if (test_is_blank_test(*t)) return;
216  variablize_symbol (thisAgent, (Symbol **) t);
217  /* Warning: this relies on the representation of tests */
218  return;
219  }
220 
221  ct = complex_test_from_test(*t);
222 
223  switch (ct->type) {
224  case GOAL_ID_TEST:
225  case IMPASSE_ID_TEST:
226  case DISJUNCTION_TEST:
227  return;
228  case CONJUNCTIVE_TEST:
229  for (c=ct->data.conjunct_list; c!=NIL; c=c->rest)
230  variablize_test (thisAgent, reinterpret_cast<test *>(&(c->first)));
231  return;
232  default: /* relational tests other than equality */
233  variablize_symbol (thisAgent, &(ct->data.referent));
234  return;
235  }
236 }
237 
238 void variablize_condition_list (agent* thisAgent, condition *cond) {
239  for (; cond!=NIL; cond=cond->next) {
240  switch (cond->type) {
241  case POSITIVE_CONDITION:
242  case NEGATIVE_CONDITION:
243  variablize_test (thisAgent, &(cond->data.tests.id_test));
244  variablize_test (thisAgent, &(cond->data.tests.attr_test));
245  variablize_test (thisAgent, &(cond->data.tests.value_test));
246  break;
248  variablize_condition_list (thisAgent, cond->data.ncc.top);
249  break;
250  }
251  }
252 }
253 
254 action *copy_and_variablize_result_list (agent* thisAgent, preference *pref, bool variablize) {
255  action *a;
256  Symbol *id, *attr, *val, *ref;
257 
258  if (!pref) return NIL;
259  allocate_with_pool (thisAgent, &thisAgent->action_pool, &a);
260  a->type = MAKE_ACTION;
261 
262  id = pref->id;
263  attr = pref->attr;
264  val = pref->value;
265  ref = pref->referent;
266 
267  symbol_add_ref (id);
268  symbol_add_ref (attr);
269  symbol_add_ref (val);
270 
271  if (variablize) {
272  variablize_symbol (thisAgent, &id);
273  variablize_symbol (thisAgent, &attr);
274  variablize_symbol (thisAgent, &val);
275  }
276 
277  a->id = symbol_to_rhs_value (id);
278  a->attr = symbol_to_rhs_value (attr);
279  a->value = symbol_to_rhs_value (val);
280 
281  a->preference_type = pref->type;
282 
283  if (preference_is_binary(pref->type)) {
284  symbol_add_ref (ref);
285  if (variablize) {
286  variablize_symbol (thisAgent, &ref);
287  }
288  a->referent = symbol_to_rhs_value (ref);
289  }
290 
291  a->next = copy_and_variablize_result_list (thisAgent, pref->next_result, variablize);
292  return a;
293 }
294 
295 /* ====================================================================
296 
297  Chunk Conditions, and Chunk Conditions Set Manipulation Routines
298 
299  These structures have two uses. First, for every ground condition,
300  one of these structures maintains certain information about it--
301  pointers to the original (instantiation's) condition, the chunks's
302  instantiation's condition, and the variablized condition, etc.
303 
304  Second, for negated conditions, these structures are entered into
305  a hash table with keys hash_condition(thisAgent, this_cond). This hash
306  table is used so we can add a new negated condition to the set of
307  negated potentials quickly--we don't want to add a duplicate of a
308  negated condition that's already there, and the hash table lets us
309  quickly determine whether a duplicate is already there.
310 
311  I used one type of structure for both of these uses, (1) for simplicity
312  and (2) to avoid having to do a second allocation when we move
313  negated conditions over to the ground set.
314 ==================================================================== */
315 
316 /* --------------------------------------------------------------------
317  Chunk Cond Set Routines
318 
319  Init_chunk_cond_set() initializes a given chunk_cond_set to be empty.
320 
321  Make_chunk_cond_for_condition() takes a condition and returns a
322  chunk_cond for it, for use in a chunk_cond_set. This is used only
323  for the negated conditions, not grounds.
324 
325  Add_to_chunk_cond_set() adds a given chunk_cond to a given chunk_cond_set
326  and returns TRUE if the condition isn't already in the set. If the
327  condition is already in the set, the routine deallocates the given
328  chunk_cond and returns FALSE.
329 
330  Remove_from_chunk_cond_set() removes a given chunk_cond from a given
331  chunk_cond_set, but doesn't deallocate it.
332 -------------------------------------------------------------------- */
333 
334  /* set of all negated conditions we encounter
335  during backtracing--these are all potentials
336  and (some of them) are added to the grounds
337  in one pass at the end of the backtracing */
338 
340  int i;
341 
342  set->all = NIL;
343  for (i=0; i<CHUNK_COND_HASH_TABLE_SIZE; i++) set->table[i] = NIL;
344 }
345 
347  chunk_cond *cc;
348  uint32_t remainder, hv;
349 
350  allocate_with_pool (thisAgent, &thisAgent->chunk_cond_pool, &cc);
351  cc->cond = cond;
352  cc->hash_value = hash_condition (thisAgent, cond);
353  remainder = cc->hash_value;
354  hv = 0;
355  while (remainder) {
356  hv ^= (remainder &
358  remainder = remainder >> LOG_2_CHUNK_COND_HASH_TABLE_SIZE;
359  }
360  cc->compressed_hash_value = hv;
361  return cc;
362 }
363 
365  chunk_cond *old;
366 
367  for (old=set->table[new_cc->compressed_hash_value]; old!=NIL;
368  old=old->next_in_bucket)
369  if (old->hash_value==new_cc->hash_value)
370  if (conditions_are_equal (old->cond, new_cc->cond))
371  break;
372  if (old) {
373  /* --- the new condition was already in the set; so don't add it --- */
374  free_with_pool (&thisAgent->chunk_cond_pool, new_cc);
375  return FALSE;
376  }
377  /* --- add new_cc to the table --- */
378  insert_at_head_of_dll (set->all, new_cc, next, prev);
379  insert_at_head_of_dll (set->table[new_cc->compressed_hash_value], new_cc,
380  next_in_bucket, prev_in_bucket);
381  return TRUE;
382 }
383 
385  remove_from_dll (set->all, cc, next, prev);
386  remove_from_dll (set->table[cc->compressed_hash_value],
387  cc, next_in_bucket, prev_in_bucket);
388 }
389 
390 /* ====================================================================
391 
392  Other Miscellaneous Chunking Routines
393 
394 ==================================================================== */
395 
396 /* --------------------------------------------------------------------
397  Build Chunk Conds For Grounds And Add Negateds
398 
399  This routine is called once backtracing is finished. It goes through
400  the ground conditions and builds a chunk_cond (see above) for each
401  one. The chunk_cond includes two new copies of the condition: one
402  to be used for the initial instantiation of the chunk, and one to
403  be (variablized and) used for the chunk itself.
404 
405  This routine also goes through the negated conditions and adds to
406  the ground set (again building chunk_cond's) any negated conditions
407  that are connected to the grounds.
408 
409  At exit, the "dest_top" and "dest_bottom" arguments are set to point
410  to the first and last chunk_cond in the ground set. The "tc_to_use"
411  argument is the tc number that this routine will use to mark the
412  TC of the ground set. At exit, this TC indicates the set of identifiers
413  in the grounds. (This is used immediately afterwards to figure out
414  which Nots must be added to the chunk.)
415 -------------------------------------------------------------------- */
416 
418  chunk_cond **dest_top,
419  chunk_cond **dest_bottom,
420  tc_number tc_to_use,
421  bool *reliable) {
422  cons *c;
423  condition *ground;
424  chunk_cond *cc, *first_cc, *prev_cc;
425 
426  first_cc = NIL; /* unnecessary, but gcc -Wall warns without it */
427 
428  /* --- build instantiated conds for grounds and setup their TC --- */
429  prev_cc = NIL;
430  while (thisAgent->grounds) {
431  c = thisAgent->grounds;
432  thisAgent->grounds = thisAgent->grounds->rest;
433  ground = static_cast<condition_struct *>(c->first);
434  free_cons (thisAgent, c);
435  /* --- make the instantiated condition --- */
436  allocate_with_pool (thisAgent, &thisAgent->chunk_cond_pool, &cc);
437  cc->cond = ground;
438  cc->instantiated_cond = copy_condition (thisAgent, cc->cond);
439  cc->variablized_cond = copy_condition (thisAgent, cc->cond);
440  if (prev_cc) {
441  prev_cc->next = cc;
442  cc->prev = prev_cc;
443  cc->variablized_cond->prev = prev_cc->variablized_cond;
444  prev_cc->variablized_cond->next = cc->variablized_cond;
445  cc->instantiated_cond->prev = prev_cc->instantiated_cond;
446  prev_cc->instantiated_cond->next = cc->instantiated_cond;
447  } else {
448  first_cc = cc;
449  cc->prev = NIL;
450  cc->variablized_cond->prev = NIL;
451  cc->instantiated_cond->prev = NIL;
452  }
453  prev_cc = cc;
454  /* --- add this in to the TC --- */
455  add_cond_to_tc (thisAgent, ground, tc_to_use, NIL, NIL);
456  }
457 
458  /* --- scan through negated conditions and check which ones are connected
459  to the grounds --- */
460  if (thisAgent->sysparams[TRACE_BACKTRACING_SYSPARAM])
461  print_string (thisAgent, "\n\n*** Adding Grounded Negated Conditions ***\n");
462 
463  while (thisAgent->negated_set.all) {
464  cc = thisAgent->negated_set.all;
465  remove_from_chunk_cond_set (&thisAgent->negated_set, cc);
466  if (cond_is_in_tc (thisAgent, cc->cond, tc_to_use)) {
467  /* --- negated cond is in the TC, so add it to the grounds --- */
468  if (thisAgent->sysparams[TRACE_BACKTRACING_SYSPARAM]) {
469  print_string (thisAgent, "\n-->Moving to grounds: ");
470  print_condition (thisAgent, cc->cond);
471  }
472  cc->instantiated_cond = copy_condition (thisAgent, cc->cond);
473  cc->variablized_cond = copy_condition (thisAgent, cc->cond);
474  if (prev_cc) {
475  prev_cc->next = cc;
476  cc->prev = prev_cc;
477  cc->variablized_cond->prev = prev_cc->variablized_cond;
478  prev_cc->variablized_cond->next = cc->variablized_cond;
479  cc->instantiated_cond->prev = prev_cc->instantiated_cond;
480  prev_cc->instantiated_cond->next = cc->instantiated_cond;
481  } else {
482  first_cc = cc;
483  cc->prev = NIL;
484  cc->variablized_cond->prev = NIL;
485  cc->instantiated_cond->prev = NIL;
486  }
487  prev_cc = cc;
488  } else {
489  /* --- not in TC, so discard the condition --- */
490 
492  {
493  // this chunk will be overgeneral! don't create it
494 
495  // SBW 5/07
496  // report what local negations are preventing the chunk,
497  // and set flags like we saw a ^quiescence t so it won't be created
498  report_local_negation ( thisAgent, cc->cond ); // in backtrace.cpp
499  *reliable = false;
500  }
501 
502  free_with_pool (&thisAgent->chunk_cond_pool, cc);
503  }
504  }
505 
506  if (prev_cc) {
507  prev_cc->next = NIL;
508  prev_cc->variablized_cond->next = NIL;
509  prev_cc->instantiated_cond->next = NIL;
510  } else {
511  first_cc = NIL;
512  }
513 
514  *dest_top = first_cc;
515  *dest_bottom = prev_cc;
516 }
517 
518 /* --------------------------------------------------------------------
519  Get Nots For Instantiated Conditions
520 
521  This routine looks through all the Nots in the instantiations in
522  instantiations_with_nots, and returns copies of the ones involving
523  pairs of identifiers in the grounds. Before this routine is called,
524  the ids in the grounds must be marked with "tc_of_grounds."
525 -------------------------------------------------------------------- */
526 
528  list *instantiations_with_nots,
529  tc_number tc_of_grounds) {
530  cons *c;
531  instantiation *inst;
532  not_struct *n1, *n2, *new_not, *collected_nots;
533 
534  /* --- collect nots for which both id's are marked --- */
535  collected_nots = NIL;
536  while (instantiations_with_nots) {
537  c = instantiations_with_nots;
538  instantiations_with_nots = c->rest;
539  inst = static_cast<instantiation_struct *>(c->first);
540  free_cons (thisAgent, c);
541  for (n1=inst->nots; n1 != NIL; n1=n1->next) {
542  /* --- Are both id's marked? If no, goto next loop iteration --- */
543  if (n1->s1->id.tc_num != tc_of_grounds) continue;
544  if (n1->s2->id.tc_num != tc_of_grounds) continue;
545  /* --- If the pair already in collected_nots, goto next iteration --- */
546  for (n2=collected_nots; n2!=NIL; n2=n2->next) {
547  if ((n2->s1 == n1->s1) && (n2->s2 == n1->s2)) break;
548  if ((n2->s1 == n1->s2) && (n2->s2 == n1->s1)) break;
549  }
550  if (n2) continue;
551  /* --- Add the pair to collected_nots --- */
552  allocate_with_pool (thisAgent, &thisAgent->not_pool, &new_not);
553  new_not->next = collected_nots;
554  collected_nots = new_not;
555  new_not->s1 = n1->s1;
556  symbol_add_ref (new_not->s1);
557  new_not->s2 = n1->s2;
558  symbol_add_ref (new_not->s2);
559  } /* end of for n1 */
560  } /* end of while instantiations_with_nots */
561 
562  return collected_nots;
563 }
564 
565 /* --------------------------------------------------------------------
566  Variablize Nots And Insert Into Conditions
567 
568  This routine goes through the given list of Nots and, for each one,
569  inserts a variablized copy of it into the given condition list at
570  the earliest possible location. (The given condition list should
571  be the previously-variablized condition list that will become the
572  chunk's LHS.) The given condition list is destructively modified;
573  the given Not list is unchanged.
574 -------------------------------------------------------------------- */
575 
577  not_struct *nots,
578  condition *conds) {
579  not_struct *n;
580  Symbol *var1, *var2;
581  test t;
582  complex_test *ct;
583  condition *c;
584  Bool added_it;
585 
586  for (n=nots; n!=NIL; n=n->next) {
587  var1 = n->s1->id.variablization;
588  var2 = n->s2->id.variablization;
589  /* --- find where var1 is bound, and add "<> var2" to that test --- */
590  allocate_with_pool (thisAgent, &thisAgent->complex_test_pool, &ct);
592  ct->type = NOT_EQUAL_TEST;
593  ct->data.referent = var2;
594  symbol_add_ref (var2);
595  added_it = FALSE;
596  for (c=conds; c!=NIL; c=c->next) {
597  if (c->type != POSITIVE_CONDITION) continue;
599  var1)) {
600  add_new_test_to_test (thisAgent, &(c->data.tests.id_test), t);
601  added_it = TRUE;
602  break;
603  }
605  var1)) {
606  add_new_test_to_test (thisAgent, &(c->data.tests.attr_test), t);
607  added_it = TRUE;
608  break;
609  }
611  var1)) {
612  add_new_test_to_test (thisAgent, &(c->data.tests.value_test), t);
613  added_it = TRUE;
614  break;
615  }
616  }
617  if (!added_it) {
618  char msg[BUFFER_MSG_SIZE];
619  strncpy (msg,"chunk.c: Internal error: couldn't add Not test to chunk\n", BUFFER_MSG_SIZE);
620  msg[BUFFER_MSG_SIZE - 1] = 0; /* ensure null termination */
621 
622  abort_with_fatal_error(thisAgent, msg);
623  }
624  } /* end of for n=nots */
625 }
626 
627 /* --------------------------------------------------------------------
628  Add Goal or Impasse Tests
629 
630  This routine adds goal id or impasse id tests to the variablized
631  conditions. For each id in the grounds that happens to be the
632  identifier of a goal or impasse, we add a goal/impasse id test
633  to the variablized conditions, to make sure that in the resulting
634  chunk, the variablization of that id is constrained to match against
635  a goal/impasse. (Note: actually, in the current implementation of
636  chunking, it's impossible for an impasse id to end up in the ground
637  set. So part of this code is unnecessary.)
638 -------------------------------------------------------------------- */
639 
640 void add_goal_or_impasse_tests (agent* thisAgent, chunk_cond *all_ccs) {
641  chunk_cond *cc;
642  tc_number tc; /* mark each id as we add a test for it, so we don't add
643  a test for the same id in two different places */
644  Symbol *id;
645  test t;
646  complex_test *ct;
647 
648  tc = get_new_tc_number(thisAgent);
649  for (cc=all_ccs; cc!=NIL; cc=cc->next) {
650  if (cc->instantiated_cond->type!=POSITIVE_CONDITION) continue;
652  if ( (id->id.isa_goal || id->id.isa_impasse) &&
653  (id->id.tc_num != tc) ) {
654  allocate_with_pool (thisAgent, &thisAgent->complex_test_pool, &ct);
655  ct->type = static_cast<byte>((id->id.isa_goal) ? GOAL_ID_TEST : IMPASSE_ID_TEST);
657  add_new_test_to_test (thisAgent, &(cc->variablized_cond->data.tests.id_test), t);
658  id->id.tc_num = tc;
659  }
660  }
661 }
662 
663 /* --------------------------------------------------------------------
664  Reorder Instantiated Conditions
665 
666  The Rete routines require the instantiated conditions (on the
667  instantiation structure) to be in the same order as the original
668  conditions from which the Rete was built. This means that the
669  initial instantiation of the chunk must have its conditions in
670  the same order as the variablized conditions. The trouble is,
671  the variablized conditions get rearranged by the reorderer. So,
672  after reordering, we have to rearrange the instantiated conditions
673  to put them in the same order as the now-scrambled variablized ones.
674  This routine does this.
675 
676  Okay, so the obvious way is to have each variablized condition (VCond)
677  point to the corresponding instantiated condition (ICond). Then after
678  reordering the VConds, we'd scan through the VConds and say
679  VCond->Icond->next = VCond->next->Icond
680  VCond->Icond->prev = VCond->prev->Icond
681  (with some extra checks for the first and last VCond in the list).
682 
683  The problem with this is that it takes an extra 4 bytes per condition,
684  for the "ICond" field. Conditions were taking up a lot of memory in
685  my test cases, so I wanted to shrink them. This routine avoids needing
686  the 4 extra bytes by using the following trick: first "swap out" 4
687  bytes from each VCond; then use that 4 bytes for the "ICond" field.
688  Now run the above algorithm. Finally, swap those original 4 bytes
689  back in.
690 -------------------------------------------------------------------- */
691 
693  condition **dest_inst_top,
694  condition **dest_inst_bottom) {
695  chunk_cond *cc;
696 
697  /* --- Step 1: swap prev pointers out of variablized conds into chunk_conds,
698  and swap pointer to the corresponding instantiated conds into the
699  variablized conds' prev pointers --- */
700  for (cc=top_cc; cc!=NIL; cc=cc->next) {
703  }
704 
705  /* --- Step 2: do the reordering of the instantiated conds --- */
706  for (cc=top_cc; cc!=NIL; cc=cc->next) {
707  if (cc->variablized_cond->next) {
709  } else {
710  cc->instantiated_cond->next = NIL;
711  *dest_inst_bottom = cc->instantiated_cond;
712  }
713 
715  cc->instantiated_cond->prev =
717  } else {
718  cc->instantiated_cond->prev = NIL;
719  *dest_inst_top = cc->instantiated_cond;
720  }
721  }
722 
723  /* --- Step 3: restore the prev pointers on variablized conds --- */
724  for (cc=top_cc; cc!=NIL; cc=cc->next) {
726  }
727 }
728 
729 /* --------------------------------------------------------------------
730  Make Clones of Results
731 
732  When we build the initial instantiation of the new chunk, we have
733  to fill in preferences_generated with *copies* of all the result
734  preferences. These copies are clones of the results. This routine
735  makes these clones and fills in chunk_inst->preferences_generated.
736 -------------------------------------------------------------------- */
737 
738 void make_clones_of_results (agent* thisAgent, preference *results,
739  instantiation *chunk_inst) {
740  preference *p, *result_p;
741 
742  chunk_inst->preferences_generated = NIL;
743  for (result_p=results; result_p!=NIL; result_p=result_p->next_result) {
744  /* --- copy the preference --- */
745  p = make_preference (thisAgent, result_p->type, result_p->id, result_p->attr,
746  result_p->value, result_p->referent);
747  symbol_add_ref (p->id);
748  symbol_add_ref (p->attr);
749  symbol_add_ref (p->value);
750  if (preference_is_binary(p->type))
752  /* --- put it onto the list for chunk_inst --- */
753  p->inst = chunk_inst;
755  inst_next, inst_prev);
756  /* --- insert it into the list of clones for this preference --- */
757  p->next_clone = result_p;
758  p->prev_clone = result_p->prev_clone;
759  result_p->prev_clone = p;
760  if (p->prev_clone) p->prev_clone->next_clone = p;
761  }
762 }
763 
764 /* kjh (B14) begin */
766  Symbol *g;
767 
768  for (g = thisAgent->top_goal; g != NIL; g = g->id.lower_goal)
769  if (g->id.level == level)
770  return(g);
771  return(NIL);
772 }
773 
775  wme *w;
776 
777  for (w = id->id.impasse_wmes; w != NIL; w = w->next)
778  if (w->attr == attr) return w->value;
779  return NIL;
780 }
781 
783 #define BUFFER_GEN_CHUNK_NAME_SIZE 512
784  char name[BUFFER_GEN_CHUNK_NAME_SIZE];
785 #define BUFFER_IMPASS_NAME_SIZE 32
786  char impass_name[BUFFER_IMPASS_NAME_SIZE];
787  Symbol *generated_name;
788  Symbol *goal;
789  byte impasse_type;
790  preference *p;
791  goal_stack_level lowest_result_level;
792 
793  if (!thisAgent->sysparams[USE_LONG_CHUNK_NAMES])
794  return(generate_new_sym_constant (thisAgent, thisAgent->chunk_name_prefix,
795  &thisAgent->chunk_count));
796 
797  lowest_result_level = thisAgent->top_goal->id.level;
798  for (p=inst->preferences_generated; p!=NIL; p=p->inst_next)
799  if (p->id->id.level > lowest_result_level)
800  lowest_result_level = p->id->id.level;
801 
802  goal = find_goal_at_goal_stack_level(thisAgent, lowest_result_level);
803 
804  if (goal) {
805  impasse_type = type_of_existing_impasse (thisAgent, goal);
806  switch (impasse_type) {
807  case NONE_IMPASSE_TYPE:
808  #ifdef DEBUG_CHUNK_NAMES
809  print ("Warning: impasse_type is NONE_IMPASSE_TYPE during chunk creation.\n");
810  xml_generate_warning(thisAgent, "Warning: impasse_type is NONE_IMPASSE_TYPE during chunk creation.");
811  #endif
812  strncpy(impass_name,"unknownimpasse",BUFFER_IMPASS_NAME_SIZE);
813  break;
815  strncpy(impass_name,"cfailure",BUFFER_IMPASS_NAME_SIZE);
816  break;
818  strncpy(impass_name,"conflict",BUFFER_IMPASS_NAME_SIZE);
819  break;
820  case TIE_IMPASSE_TYPE:
821  strncpy(impass_name,"tie",BUFFER_IMPASS_NAME_SIZE);
822  break;
824  {
825  Symbol *sym;
826 
827  if ((sym = find_impasse_wme_value(goal->id.lower_goal,thisAgent->attribute_symbol)) == NIL) {
828  #ifdef DEBUG_CHUNK_NAMES
829  // TODO: generate warning XML: I think we need to get a string for "do_print_for_identifier" and append it
830  // but this seems low priority since it's not even included in a normal build
831  print ("Warning: Failed to find ^attribute impasse wme.\n");
832  do_print_for_identifier(goal->id.lower_goal, 1, 0, 0);
833  #endif
834  strncpy(impass_name,"unknownimpasse",BUFFER_IMPASS_NAME_SIZE);
835  } else if (sym == thisAgent->operator_symbol) {
836  strncpy(impass_name,"opnochange",BUFFER_IMPASS_NAME_SIZE);
837  } else if (sym == thisAgent->state_symbol) {
838  strncpy(impass_name,"snochange",BUFFER_IMPASS_NAME_SIZE);
839  } else {
840  #ifdef DEBUG_CHUNK_NAMES
841  print ("Warning: ^attribute impasse wme has unexpected value.\n");
842  xml_generate_warning(thisAgent, "Warning: ^attribute impasse wme has unexpected value.");
843  #endif
844  strncpy(impass_name,"unknownimpasse",BUFFER_IMPASS_NAME_SIZE);
845  }
846  }
847  break;
848  default:
849  #ifdef DEBUG_CHUNK_NAMES
850  // TODO: generate warning XML: I think we need to create a buffer and SNPRINTF the impasse_type into it (since it's a byte)
851  // but this seems low priority since it's not even included in a normal build
852  print ("Warning: encountered unknown impasse_type: %d.\n", impasse_type);
853 
854  #endif
855  strncpy(impass_name,"unknownimpasse",BUFFER_IMPASS_NAME_SIZE);
856  break;
857  }
858  } else {
859  #ifdef DEBUG_CHUNK_NAMES
860  print ("Warning: Failed to determine impasse type.\n");
861  xml_generate_warning(thisAgent, "Warning: Failed to determine impasse type.");
862  #endif
863  strncpy(impass_name,"unknownimpasse",BUFFER_IMPASS_NAME_SIZE);
864  }
865  impass_name[BUFFER_IMPASS_NAME_SIZE - 1] = 0; /* ensure null termination */
866 
867  SNPRINTF (name, BUFFER_GEN_CHUNK_NAME_SIZE, "%s-%lu*d%lu*%s*%lu",
868  thisAgent->chunk_name_prefix,
869  static_cast<long unsigned int>(thisAgent->chunk_count++),
870  static_cast<long unsigned int>(thisAgent->d_cycle_count),
871  impass_name,
872  static_cast<long unsigned int>(thisAgent->chunks_this_d_cycle)
873  );
874  name[BUFFER_GEN_CHUNK_NAME_SIZE - 1] = 0; /* ensure null termination */
875 
876 
877  /* Any user who named a production like this deserves to be burned, but we'll have mercy: */
878  if (find_sym_constant (thisAgent, name)) {
879  uint64_t collision_count;
880 
881  collision_count = 1;
882  print (thisAgent, "Warning: generated chunk name already exists. Will find unique name.\n");
883  xml_generate_warning(thisAgent, "Warning: generated chunk name already exists. Will find unique name.");
884  do {
885  SNPRINTF (name, BUFFER_GEN_CHUNK_NAME_SIZE, "%s-%lu*d%lu*%s*%lu*%lu",
886  thisAgent->chunk_name_prefix,
887  static_cast<long unsigned int>(thisAgent->chunk_count++),
888  static_cast<long unsigned int>(thisAgent->d_cycle_count),
889  impass_name,
890  static_cast<long unsigned int>(thisAgent->chunks_this_d_cycle),
891  static_cast<long unsigned int>(collision_count++)
892  );
893  name[BUFFER_GEN_CHUNK_NAME_SIZE - 1] = 0; /* ensure null termination */
894 
895  } while (find_sym_constant (thisAgent, name));
896  }
897 
898  generated_name = make_sym_constant(thisAgent, name);
899  return generated_name;
900 }
901 /* kjh (B14) end */
902 
903 bool should_variablize(agent *thisAgent, instantiation *inst) {
904  preference *p;
905 
906  if ( thisAgent->sysparams[LEARNING_ON_SYSPARAM] == 0 )
907  {
908  return false;
909  }
910 
911  if ( thisAgent->sysparams[LEARNING_EXCEPT_SYSPARAM] &&
913  {
914  if (thisAgent->soar_verbose_flag || thisAgent->sysparams[TRACE_CHUNKS_SYSPARAM])
915  {
916  char buf[64];
917  std::ostringstream message;
918  message << "\nnot chunking due to chunk-free state " << symbol_to_string(thisAgent, inst->match_goal, false, buf, 64);
919  print(thisAgent, message.str().c_str());
920  xml_generate_verbose(thisAgent, message.str().c_str());
921  }
922  return false;
923  }
924 
925  if (thisAgent->sysparams[LEARNING_ONLY_SYSPARAM] &&
927  {
928  if (thisAgent->soar_verbose_flag || thisAgent->sysparams[TRACE_CHUNKS_SYSPARAM])
929  {
930  char buf[64];
931  std::ostringstream message;
932  message << "\nnot chunking due to non-chunky state " << symbol_to_string(thisAgent, inst->match_goal, false, buf, 64);
933  print(thisAgent, message.str().c_str());
934  xml_generate_verbose(thisAgent, message.str().c_str());
935  }
936  return false;
937  }
938 
939  /* allow_bottom_up_chunks will be false if a chunk was already
940  learned in a lower goal
941  */
942  if (!thisAgent->sysparams[LEARNING_ALL_GOALS_SYSPARAM] &&
944  {
945  return false;
946  }
947 
948  /* if a result is created in a state higher than the immediate
949  superstate, don't make chunks for intermediate justifications.
950  */
951  for (p=inst->preferences_generated; p; p=p->inst_next)
952  {
953  if (p->id->id.level < inst->match_goal_level-1)
954  {
955  return false;
956  }
957  }
958 
959  return true;
960 }
961 
962 /* ====================================================================
963 
964  Chunk Instantiation
965 
966  This the main chunking routine. It takes an instantiation, and a
967  flag "variablize"--if FALSE, the chunk will not be
968  variablized. (If TRUE, it may still not be variablized, due to
969  chunk-free-problem-spaces, ^quiescence t, etc.)
970 ==================================================================== */
971 
972 
973 void chunk_instantiation (agent* thisAgent, instantiation *inst, bool dont_variablize, instantiation **custom_inst_list)
974 {
975  goal_stack_level grounds_level;
976  preference *results, *pref;
977  action *rhs;
978  production *prod;
979  instantiation *chunk_inst;
980  Symbol *prod_name;
981  byte prod_type;
982  Bool print_name, print_prod;
983  byte rete_addition_result;
984  condition *lhs_top, *lhs_bottom;
985  not_struct *nots;
986  chunk_cond *top_cc, *bottom_cc;
987  bool reliable = true;
988  bool variablize;
989 
990  explain_chunk_str temp_explain_chunk;
991  memset(temp_explain_chunk.name, 0, EXPLAIN_CHUNK_STRUCT_NAME_BUFFER_SIZE);
992 
993 #ifndef NO_TIMING_STUFF
994 #ifdef DETAILED_TIMING_STATS
995  soar_process_timer local_timer;
996  local_timer.set_enabled( &( thisAgent->sysparams[ TIMERS_ENABLED ] ) );
997 #endif
998 #endif
999 
1000  /* --- if it only matched an attribute impasse, don't chunk --- */
1001  if (! inst->match_goal)
1002  return;
1003 
1004  /* --- if no preference is above the match goal level, exit --- */
1005  for (pref=inst->preferences_generated; pref!=NIL; pref=pref->inst_next)
1006  {
1007  if (pref->id->id.level < inst->match_goal_level)
1008  break;
1009  }
1010  if (! pref)
1011  return;
1012 
1013 #ifndef NO_TIMING_STUFF
1014 #ifdef DETAILED_TIMING_STATS
1015  local_timer.start();
1016 #endif
1017 #endif
1018 
1019  results = get_results_for_instantiation (thisAgent, inst);
1020  if (!results) goto chunking_done;
1021 
1022  /* set allow_bottom_up_chunks to false for all higher goals to prevent chunking */
1023  {
1024  Symbol *g;
1025  for (g=inst->match_goal->id.higher_goal; g && g->id.allow_bottom_up_chunks; g=g->id.higher_goal)
1027  }
1028 
1029  grounds_level = inst->match_goal_level - 1;
1030 
1031  thisAgent->backtrace_number++;
1032  if (thisAgent->backtrace_number==0)
1033  thisAgent->backtrace_number=1;
1034 
1035  thisAgent->grounds_tc++;
1036  if (thisAgent->grounds_tc==0)
1037  thisAgent->grounds_tc=1;
1038 
1039  thisAgent->potentials_tc++;
1040  if (thisAgent->potentials_tc==0)
1041  thisAgent->potentials_tc=1;
1042 
1043  thisAgent->locals_tc++;
1044  if (thisAgent->locals_tc==0)
1045  thisAgent->locals_tc=1;
1046 
1047  thisAgent->grounds = NIL;
1048  thisAgent->positive_potentials = NIL;
1049  thisAgent->locals = NIL;
1050  thisAgent->instantiations_with_nots = NIL;
1051 
1052  /* Start a new structure for this potential chunk */
1053 
1054  if (thisAgent->sysparams[EXPLAIN_SYSPARAM])
1055  {
1056  temp_explain_chunk.conds = NULL;
1057  temp_explain_chunk.actions = NULL;
1058  temp_explain_chunk.backtrace = NULL;
1059  temp_explain_chunk.name[0] = '\0';
1060  temp_explain_chunk.all_grounds = NIL;
1061  temp_explain_chunk.next_chunk = NULL;
1062  reset_backtrace_list(thisAgent);
1063  }
1064 
1065  /* --- backtrace through the instantiation that produced each result --- */
1066  for (pref=results; pref!=NIL; pref=pref->next_result)
1067  {
1068  if (thisAgent->sysparams[TRACE_BACKTRACING_SYSPARAM])
1069  {
1070  print_string (thisAgent, "\nFor result preference ");
1071  xml_begin_tag(thisAgent, kTagBacktraceResult);
1072  print_preference (thisAgent, pref);
1073  print_string (thisAgent, " ");
1074  }
1075  backtrace_through_instantiation (thisAgent, pref->inst, grounds_level, NULL, &reliable, 0);
1076 
1077  if (thisAgent->sysparams[TRACE_BACKTRACING_SYSPARAM])
1078  {
1079  xml_end_tag(thisAgent, kTagBacktraceResult);
1080  }
1081  }
1082 
1083  while (TRUE)
1084  {
1085  trace_locals (thisAgent, grounds_level, &reliable);
1086  trace_grounded_potentials (thisAgent);
1087  if (! trace_ungrounded_potentials (thisAgent, grounds_level, &reliable)) break;
1088  }
1089 
1090  free_list (thisAgent, thisAgent->positive_potentials);
1091 
1092  /* --- backtracing done; collect the grounds into the chunk --- */
1093  {
1094  tc_number tc_for_grounds;
1095  tc_for_grounds = get_new_tc_number(thisAgent);
1096  build_chunk_conds_for_grounds_and_add_negateds (thisAgent, &top_cc, &bottom_cc, tc_for_grounds, &reliable);
1097  nots = get_nots_for_instantiated_conditions (thisAgent, thisAgent->instantiations_with_nots, tc_for_grounds);
1098  }
1099 
1100  variablize = !dont_variablize && reliable && should_variablize(thisAgent, inst);
1101 
1102  /* --- check for LTI validity --- */
1103  if ( variablize )
1104  {
1105  if ( top_cc )
1106  {
1107  // need a temporary copy of the actions
1108  thisAgent->variablization_tc = get_new_tc_number(thisAgent);
1109  rhs = copy_and_variablize_result_list (thisAgent, results, true);
1110 
1111  if ( !smem_valid_production( top_cc->variablized_cond, rhs ) )
1112  {
1113  variablize = false;
1114  if (thisAgent->sysparams[TRACE_BACKTRACING_SYSPARAM])
1115  {
1116  print( thisAgent, "\nWarning: LTI validation failed, creating justification instead." );
1117  xml_generate_warning( thisAgent, "LTI validation failed, creating justification instead." );
1118  }
1119  }
1120 
1121  // remove temporary copy
1122  deallocate_action_list (thisAgent, rhs);
1123  }
1124  }
1125 
1126  /* --- get symbol for name of new chunk or justification --- */
1127  if (variablize)
1128  {
1129  /* kjh (B14) begin */
1130  thisAgent->chunks_this_d_cycle++;
1131  prod_name = generate_chunk_name_sym_constant(thisAgent, inst);
1132  /* kjh (B14) end */
1133 
1134  /* old way of generating chunk names ...
1135  prod_name = generate_new_sym_constant ("chunk-",&thisAgent->chunk_count);
1136  thisAgent->chunks_this_d_cycle)++;
1137  */
1138 
1139  prod_type = CHUNK_PRODUCTION_TYPE;
1140  print_name = (thisAgent->sysparams[TRACE_CHUNK_NAMES_SYSPARAM] != 0);
1141  print_prod = (thisAgent->sysparams[TRACE_CHUNKS_SYSPARAM] != 0);
1142  }
1143  else
1144  {
1145  prod_name = generate_new_sym_constant (thisAgent, "justification-", &thisAgent->justification_count);
1146  prod_type = JUSTIFICATION_PRODUCTION_TYPE;
1147  print_name = (thisAgent->sysparams[TRACE_JUSTIFICATION_NAMES_SYSPARAM] != 0);
1148  print_prod = (thisAgent->sysparams[TRACE_JUSTIFICATIONS_SYSPARAM] != 0);
1149  }
1150 
1151  /* AGR 617/634 begin */
1152  if (print_name)
1153  {
1154  if (get_printer_output_column(thisAgent)!=1)
1155  print (thisAgent, "\n");
1156  print_with_symbols (thisAgent, "Building %y", prod_name);
1157 
1158  xml_begin_tag(thisAgent, kTagLearning);
1159  xml_begin_tag(thisAgent, kTagProduction);
1160  xml_att_val(thisAgent, kProduction_Name, prod_name);
1161  xml_end_tag(thisAgent, kTagProduction);
1162  xml_end_tag(thisAgent, kTagLearning);
1163  }
1164  /* AGR 617/634 end */
1165 
1166  /* --- if there aren't any grounds, exit --- */
1167  if (! top_cc)
1168  {
1169  if (thisAgent->sysparams[PRINT_WARNINGS_SYSPARAM])
1170  {
1171  print_string (thisAgent, " Warning: chunk has no grounds, ignoring it.");
1172  xml_generate_warning(thisAgent, "Warning: chunk has no grounds, ignoring it.");
1173  }
1174 
1175  symbol_remove_ref(thisAgent, prod_name);
1176  goto chunking_done;
1177  }
1178 
1179  /* MVP 6-8-94 */
1180  if (thisAgent->chunks_this_d_cycle > static_cast<uint64_t>(thisAgent->sysparams[MAX_CHUNKS_SYSPARAM]) )
1181  {
1182  if (thisAgent->sysparams[PRINT_WARNINGS_SYSPARAM])
1183  {
1184  print (thisAgent, "\nWarning: reached max-chunks! Halting system.");
1185  xml_generate_warning(thisAgent, "Warning: reached max-chunks! Halting system.");
1186  }
1187  thisAgent->max_chunks_reached = TRUE;
1188 
1189  symbol_remove_ref(thisAgent, prod_name);
1190  goto chunking_done;
1191  }
1192 
1193  lhs_top = top_cc->variablized_cond;
1194  lhs_bottom = bottom_cc->variablized_cond;
1195  if (variablize) {
1196  reset_variable_generator (thisAgent, lhs_top, NIL);
1197  thisAgent->variablization_tc = get_new_tc_number(thisAgent);
1198  variablize_condition_list (thisAgent, lhs_top);
1199  variablize_nots_and_insert_into_conditions (thisAgent, nots, lhs_top);
1200  }
1201  rhs = copy_and_variablize_result_list (thisAgent, results, variablize);
1202 
1203  add_goal_or_impasse_tests (thisAgent, top_cc);
1204 
1205  prod = make_production (thisAgent, prod_type, prod_name, &lhs_top, &lhs_bottom, &rhs, FALSE);
1206 
1207  if (!prod)
1208  {
1209  print (thisAgent, "\nUnable to reorder this chunk:\n ");
1210  print_condition_list (thisAgent, lhs_top, 2, FALSE);
1211  print (thisAgent, "\n -->\n ");
1212  print_action_list (thisAgent, rhs, 3, FALSE);
1213  print (thisAgent, "\n\nThis error is likely caused by the reasons outlined section 4 of the Soar\n");
1214  print (thisAgent, "manual, subsection \"revising the substructure of a previous result\".\n");
1215  print (thisAgent, "\n");
1216  print (thisAgent, "Check that the rules are not revising substructure of a result matched only\n");
1217  print (thisAgent, "through the local state.\n");
1218 
1219  deallocate_condition_list (thisAgent, top_cc->variablized_cond);
1220  deallocate_condition_list (thisAgent, top_cc->instantiated_cond);
1221  {
1222  chunk_cond *cc;
1223  while (top_cc)
1224  {
1225  cc = top_cc;
1226  top_cc = cc->next;
1227  free_with_pool (&thisAgent->chunk_cond_pool, cc);
1228  }
1229  }
1230 
1231  deallocate_action_list (thisAgent, rhs);
1232  symbol_remove_ref(thisAgent, prod_name);
1233 
1234  // We cannot proceed, the GDS will crash in decide.cpp:decide_non_context_slot
1235  thisAgent->stop_soar = TRUE;
1236  thisAgent->system_halted = TRUE;
1237 
1238  goto chunking_done;
1239  }
1240 
1241  {
1242  condition *inst_lhs_top = 0, *inst_lhs_bottom = 0;
1243 
1244  reorder_instantiated_conditions (top_cc, &inst_lhs_top, &inst_lhs_bottom);
1245 
1246  /* Record the list of grounds in the order they will appear in the chunk. */
1247  if (thisAgent->sysparams[EXPLAIN_SYSPARAM])
1248  temp_explain_chunk.all_grounds = inst_lhs_top; /* Not a copy yet */
1249 
1250  allocate_with_pool (thisAgent, &thisAgent->instantiation_pool, &chunk_inst);
1251  chunk_inst->prod = prod;
1252  chunk_inst->top_of_instantiated_conditions = inst_lhs_top;
1253  chunk_inst->bottom_of_instantiated_conditions = inst_lhs_bottom;
1254  chunk_inst->nots = nots;
1255 
1256  chunk_inst->GDS_evaluated_already = FALSE; /* REW: 09.15.96 */
1257 
1258  chunk_inst->reliable = reliable;
1259 
1260  chunk_inst->in_ms = TRUE; /* set TRUE for now, we'll find out later... */
1261  make_clones_of_results (thisAgent, results, chunk_inst);
1262  fill_in_new_instantiation_stuff (thisAgent, chunk_inst, TRUE);
1263  }
1264 
1265  /* RBD 4/6/95 Need to copy cond's and actions for the production here,
1266  otherwise some of the variables might get deallocated by the call to
1267  add_production_to_rete() when it throws away chunk variable names. */
1268  if (thisAgent->sysparams[EXPLAIN_SYSPARAM])
1269  {
1270  condition *new_top = 0;
1271  condition *new_bottom = 0;
1272  copy_condition_list (thisAgent, lhs_top, &new_top, &new_bottom);
1273  temp_explain_chunk.conds = new_top;
1274  temp_explain_chunk.actions = copy_and_variablize_result_list (thisAgent, results, variablize);
1275  }
1276 
1277  rete_addition_result = add_production_to_rete (thisAgent, prod, lhs_top, chunk_inst, print_name);
1278 
1279  /* If didn't immediately excise the chunk from the rete net
1280  then record the temporary structure in the list of explained chunks. */
1281 
1282  if (thisAgent->sysparams[EXPLAIN_SYSPARAM])
1283  {
1284  if ((rete_addition_result != DUPLICATE_PRODUCTION)
1285  && ((prod_type != JUSTIFICATION_PRODUCTION_TYPE)
1286  || (rete_addition_result != REFRACTED_INST_DID_NOT_MATCH) ))
1287  {
1288  strncpy(temp_explain_chunk.name,prod_name->sc.name, EXPLAIN_CHUNK_STRUCT_NAME_BUFFER_SIZE);
1289  temp_explain_chunk.name[EXPLAIN_CHUNK_STRUCT_NAME_BUFFER_SIZE - 1] = 0;
1290  explain_add_temp_to_chunk_list (thisAgent, &temp_explain_chunk);
1291  }
1292  else
1293  {
1294  /* RBD 4/6/95 if excised the chunk, discard previously-copied stuff */
1295  deallocate_condition_list (thisAgent, temp_explain_chunk.conds);
1296  deallocate_action_list (thisAgent, temp_explain_chunk.actions);
1297  }
1298  }
1299 
1300  /* --- deallocate chunks conds and variablized conditions --- */
1301  deallocate_condition_list (thisAgent, lhs_top);
1302  {
1303  chunk_cond *cc;
1304  while (top_cc)
1305  {
1306  cc = top_cc;
1307  top_cc = cc->next;
1308  free_with_pool (&thisAgent->chunk_cond_pool, cc);
1309  }
1310  }
1311 
1312  if (print_prod && (rete_addition_result!=DUPLICATE_PRODUCTION))
1313  {
1314  print_string (thisAgent, "\n");
1315  xml_begin_tag(thisAgent, kTagLearning);
1316  print_production (thisAgent, prod, FALSE);
1317  xml_end_tag(thisAgent, kTagLearning);
1318  }
1319 
1320  if (rete_addition_result==DUPLICATE_PRODUCTION)
1321  {
1322  excise_production (thisAgent, prod, FALSE);
1323  }
1324  else if ((prod_type==JUSTIFICATION_PRODUCTION_TYPE)
1325  && (rete_addition_result==REFRACTED_INST_DID_NOT_MATCH))
1326  {
1327  excise_production (thisAgent, prod, FALSE);
1328  }
1329 
1330  if (rete_addition_result!=REFRACTED_INST_MATCHED)
1331  {
1332  /* --- it didn't match, or it was a duplicate production --- */
1333  /* --- tell the firer it didn't match, so it'll only assert the
1334  o-supported preferences --- */
1335  chunk_inst->in_ms = FALSE;
1336  }
1337 
1338  /* --- assert the preferences --- */
1339  chunk_inst->next = (*custom_inst_list);
1340  (*custom_inst_list) = chunk_inst;
1341 
1342  /* MVP 6-8-94 */
1343  if (!thisAgent->max_chunks_reached)
1344  chunk_instantiation (thisAgent, chunk_inst, dont_variablize, custom_inst_list);
1345 
1346 #ifndef NO_TIMING_STUFF
1347 #ifdef DETAILED_TIMING_STATS
1348  local_timer.stop();
1349  thisAgent->timers_chunking_cpu_time[thisAgent->current_phase].update(local_timer);
1350 #endif
1351 #endif
1352 
1353  return;
1354 
1355 chunking_done: {}
1356 #ifndef NO_TIMING_STUFF
1357 #ifdef DETAILED_TIMING_STATS
1358  local_timer.stop();
1359  thisAgent->timers_chunking_cpu_time[thisAgent->current_phase].update(local_timer);
1360 #endif
1361 #endif
1362 }
1363 
1364 /* --------------------------------------------------------------------
1365 
1366  Chunker Initialization
1367 
1368  Init_chunker() is called at startup time to do initialization here.
1369  -------------------------------------------------------------------- */
1370 
1371  void init_chunker (agent* thisAgent) {
1372  init_memory_pool (thisAgent, &thisAgent->chunk_cond_pool, sizeof(chunk_cond), "chunk condition");
1373  init_chunk_cond_set (&thisAgent->negated_set);
1374  }
1375