Soar Kernel  9.3.2 08-06-12
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
wma.cpp
Go to the documentation of this file.
1 #include <portability.h>
2 
3 /*************************************************************************
4  * PLEASE SEE THE FILE "COPYING" (INCLUDED WITH THIS SOFTWARE PACKAGE)
5  * FOR LICENSE AND COPYRIGHT INFORMATION.
6  *************************************************************************/
7 
8 /*************************************************************************
9  *
10  * file: wma.cpp
11  *
12  * =======================================================================
13  * Description : Various functions for WMA
14  * =======================================================================
15  */
16 
17 #include "wma.h"
18 
19 #include <set>
20 #include <cmath>
21 #include <cstdlib>
22 
23 #include "agent.h"
24 #include "wmem.h"
25 #include "instantiations.h"
26 #include "explain.h"
27 #include "rete.h"
28 #include "decide.h"
29 #include "prefmem.h"
30 
31 #include "misc.h"
32 #include "xml.h"
33 #include "print.h"
34 
37 // Bookmark strings to help navigate the code
40 
41 // parameters wma::param
42 // stats wma::stats
43 // timers wma::timers
44 //
45 // initialization wma::init
46 //
47 // decay wma::decay
48 // forgetting wma::forget
49 // update wma::update
50 //
51 // api wma::api
52 
53 
56 // Parameter Functions (wma::params)
59 
60 void wma_init( agent *my_agent );
61 void wma_deinit( agent *my_agent );
62 
63 
64 wma_activation_param::wma_activation_param( const char *new_name, soar_module::boolean new_value, soar_module::predicate<soar_module::boolean> *new_prot_pred, agent *new_agent ): soar_module::boolean_param( new_name, new_value, new_prot_pred ), my_agent( new_agent ) {}
65 
67 {
68  if ( new_value != value )
69  {
70  value = new_value;
71 
72  if ( new_value == soar_module::on )
73  {
74  wma_init( my_agent );
75  }
76  else
77  {
79  }
80  }
81 }
82 
83 //
84 
85 wma_decay_param::wma_decay_param( const char *new_name, double new_value, soar_module::predicate<double> *new_val_pred, soar_module::predicate<double> *new_prot_pred ): soar_module::decimal_param( new_name, new_value, new_val_pred, new_prot_pred ) {}
86 
87 void wma_decay_param::set_value( double new_value ) { value = -new_value; }
88 
89 //
90 
91 template <typename T>
92 wma_activation_predicate<T>::wma_activation_predicate( agent *new_agent ): soar_module::agent_predicate<T>( new_agent ) {};
93 
94 template <typename T>
95 bool wma_activation_predicate<T>::operator() ( T /*val*/ ) { return wma_enabled( this->my_agent ); };
96 
97 //
98 
99 wma_param_container::wma_param_container( agent *new_agent ): soar_module::param_container( new_agent )
100 {
101  // WMA on/off
103  add( activation );
104 
105  // decay-rate
106  decay_rate = new wma_decay_param( "decay-rate", -0.5, new soar_module::btw_predicate<double>( 0, 1, true ), new wma_activation_predicate<double>( my_agent ) );
107  add( decay_rate );
108 
109  // decay-thresh
110  decay_thresh = new wma_decay_param( "decay-thresh", -2.0, new soar_module::gt_predicate<double>( 0, false ), new wma_activation_predicate<double>( my_agent ) );
111  add( decay_thresh );
112 
113  // do we compute an approximation of the distant references?
115  add( petrov_approx );
116 
117  // are WMEs removed from WM when activation gets too low?
119  forgetting->add_mapping( off, "off" );
120  forgetting->add_mapping( naive, "naive" );
121  forgetting->add_mapping( bsearch, "bsearch" );
122  forgetting->add_mapping( approx, "on" );
123  add( forgetting );
124 
125  // which WMEs are removed?
127  forget_wme->add_mapping( all, "all" );
128  forget_wme->add_mapping( lti, "lti" );
129  add( forget_wme );
130 
131  // fake forgetting?
133  add( fake_forgetting );
134 
135  // timer level
137  timers->add_mapping( soar_module::timer::zero, "off" );
139  add( timers );
140 
141  // max size of power cache
143  add( max_pow_cache );
144 };
145 
146 //
147 
148 bool wma_enabled( agent *my_agent )
149 {
150  return ( my_agent->wma_params->activation->get_value() == soar_module::on );
151 }
152 
155 
156 
159 // Statistic Functions (wma::stats)
162 
163 wma_stat_container::wma_stat_container( agent *new_agent ): soar_module::stat_container( new_agent )
164 {
165  // forgotten-wmes
167  add( forgotten_wmes );
168 };
169 
172 
173 
176 // Timer Functions (wma::timers)
179 
180 wma_timer_container::wma_timer_container( agent *new_agent ): soar_module::timer_container( new_agent )
181 {
182  // one
183  history = new wma_timer( "wma_history", my_agent, soar_module::timer::one );
184  add( history );
185 
186  forgetting = new wma_timer( "wma_forgetting", my_agent, soar_module::timer::one );
187  add( forgetting );
188 }
189 
190 //
191 
192 wma_timer_level_predicate::wma_timer_level_predicate( agent *new_agent ): soar_module::agent_predicate< soar_module::timer::timer_level >( new_agent ) {}
193 
195 
196 //
197 
198 wma_timer::wma_timer(const char *new_name, agent *new_agent, soar_module::timer::timer_level new_level): soar_module::timer( new_name, new_agent, new_level, new wma_timer_level_predicate( new_agent ) ) {}
199 
200 
203 // Initialization Functions (wma::init)
206 
207 void wma_init( agent *my_agent )
208 {
209  if ( my_agent->wma_initialized )
210  {
211  return;
212  }
213 
214  double decay_rate = my_agent->wma_params->decay_rate->get_value();
215  double decay_thresh = my_agent->wma_params->decay_thresh->get_value();
216  int64_t max_pow_cache = my_agent->wma_params->max_pow_cache->get_value();
217 
218  // Pre-compute the integer powers of the decay exponent in order to avoid
219  // repeated calls to pow() at runtime
220  {
221  // determine cache size
222  {
223  // computes how many powers to compute
224  // basic idea: solve for the time that would just fall below the decay threshold, given decay rate and assumption of max references/decision
225  // t = e^( ( thresh - ln( max_refs ) ) / -decay_rate )
226  double cache_full = static_cast<double>( exp( ( decay_thresh - log( static_cast<double>( WMA_REFERENCES_PER_DECISION ) ) ) / decay_rate ) );
227 
228  // we bound this by the max-pow-cache parameter to control the space vs. time tradeoff the cache supports
229  // max-pow-cache is in MB, so do the conversion:
230  // MB * 1024 bytes/KB * 1024 KB/MB
231  double cache_bound = ( static_cast<unsigned int>( max_pow_cache * 1024 * 1024 ) / static_cast<unsigned int>( sizeof( double ) ) );
232 
233  my_agent->wma_power_size = static_cast< unsigned int >( ceil( ( cache_full > cache_bound )?( cache_bound ):( cache_full ) ) );
234  }
235 
236  my_agent->wma_power_array = new double[ my_agent->wma_power_size ];
237 
238  my_agent->wma_power_array[0] = 0.0;
239  for( unsigned int i=1; i<my_agent->wma_power_size; i++ )
240  {
241  my_agent->wma_power_array[ i ] = pow( static_cast<double>( i ), decay_rate );
242  }
243  }
244 
245  // calculate the pre-log'd forgetting threshold, to avoid most
246  // calls to log
247  my_agent->wma_thresh_exp = exp( decay_thresh );
248 
249  // approximation cache
251  {
253 
254  my_agent->wma_approx_array[0] = 0;
255  for ( int i=1; i<WMA_REFERENCES_PER_DECISION; i++ )
256  {
257  my_agent->wma_approx_array[i] = static_cast< wma_d_cycle >( ceil( exp( static_cast<double>( decay_thresh - log( static_cast<double>(i) ) ) / static_cast<double>( decay_rate ) ) ) );
258  }
259  }
260 
261  // note initialization
262  my_agent->wma_initialized = true;
263 }
264 
265 void wma_deinit( agent *my_agent )
266 {
267  if ( !my_agent->wma_initialized )
268  {
269  return;
270  }
271 
272  // release power array memory
273  delete[] my_agent->wma_power_array;
274 
275  // release approximation array memory (if applicable)
277  {
278  delete[] my_agent->wma_approx_array;
279  }
280 
281  // clear touched
282  my_agent->wma_touched_elements->clear();
283  my_agent->wma_touched_sets->clear();
284 
285  // clear forgetting priority queue
286  for ( wma_forget_p_queue::iterator pq_p=my_agent->wma_forget_pq->begin(); pq_p!=my_agent->wma_forget_pq->end(); pq_p++ )
287  {
288  pq_p->second->~wma_decay_set();
289  free_with_pool( &( my_agent->wma_decay_set_pool ), pq_p->second );
290  }
291  my_agent->wma_forget_pq->clear();
292 
293  my_agent->wma_initialized = false;
294 }
295 
298 
299 
302 // Decay Functions (wma::decay)
305 
306 inline unsigned int wma_history_next( unsigned int current )
307 {
308  return ( ( current == ( WMA_DECAY_HISTORY - 1 ) )?( 0 ):( current + 1 ) );
309 }
310 
311 inline unsigned int wma_history_prev( unsigned int current )
312 {
313  return ( ( current == 0 )?( WMA_DECAY_HISTORY - 1 ):( current - 1 ) );
314 }
315 
317 {
318  return ( ( w->preference ) && ( w->preference->reference_count ) && ( w->preference->o_supported ) );
319 }
320 
321 inline double wma_pow( agent* my_agent, wma_d_cycle cycle_diff )
322 {
323  if ( cycle_diff < my_agent->wma_power_size )
324  {
325  return my_agent->wma_power_array[ cycle_diff ];
326  }
327  else
328  {
329  return pow( static_cast<double>( cycle_diff ), my_agent->wma_params->decay_rate->get_value() );
330  }
331 }
332 
333 inline double wma_sum_history( agent* my_agent, wma_history* history, wma_d_cycle current_cycle )
334 {
335  double return_val = 0.0;
336 
337  unsigned int p = history->next_p;
338  unsigned int counter = history->history_ct;
339  wma_d_cycle cycle_diff = 0;
340 
341  //
342 
343  while ( counter )
344  {
345  p = wma_history_prev( p );
346 
347  cycle_diff = ( current_cycle - history->access_history[ p ].d_cycle );
348  assert( cycle_diff > 0 );
349 
350  return_val += ( history->access_history[ p ].num_references * wma_pow( my_agent, cycle_diff ) );
351 
352  counter--;
353  }
354 
355  // see (Petrov, 2006)
356  if ( my_agent->wma_params->petrov_approx->get_value() == soar_module::on )
357  {
358  // if ( n > k )
359  if ( history->total_references > history->history_references )
360  {
361  // ( n - k ) * ( tn^(1-d) - tk^(1-d) )
362  // -----------------------------------
363  // ( 1 - d ) * ( tn - tk )
364 
365  // decay_rate is negated (for nice printing)
366  double d_inv = ( 1 + my_agent->wma_params->decay_rate->get_value() );
367 
368  return_val += ( ( ( history->total_references - history->history_references ) * ( pow( static_cast<double>( current_cycle - history->first_reference ), d_inv ) - pow( static_cast<double>( cycle_diff ), d_inv ) ) ) /
369  ( d_inv * ( ( current_cycle - history->first_reference ) - cycle_diff ) ) );
370  }
371  }
372 
373  return return_val;
374 }
375 
376 inline double wma_calculate_decay_activation( agent* my_agent, wma_decay_element* decay_el, wma_d_cycle current_cycle, bool log_result )
377 {
378  wma_history* history = &( decay_el->touches );
379 
380  if ( history->history_ct )
381  {
382  double history_sum = wma_sum_history( my_agent, history, current_cycle );
383 
384  if ( !log_result )
385  {
386  return history_sum;
387  }
388 
389  if ( history_sum > 0.0 )
390  {
391  return log( history_sum );
392  }
393  else
394  {
395  return WMA_ACTIVATION_LOW;
396  }
397  }
398  else
399  {
400  return ( ( log_result )?( WMA_ACTIVATION_LOW ):( 0.0 ) );
401  }
402 }
403 
405 {
406  wma_reference return_val = 0;
407  preference* pref;
408  condition *cond;
409  wme *cond_wme;
410  wma_pooled_wme_set::iterator wme_p;
411 
412  tc_number tc = ( my_agent->wma_tc_counter++ );
413 
414  uint64_t num_cond_wmes = 0;
415  double combined_time_sum = 0.0;
416 
417  for ( pref=w->preference->slot->preferences[ACCEPTABLE_PREFERENCE_TYPE]; pref; pref=pref->next )
418  {
419  if ( ( pref->value == w->value ) && ( pref->o_supported ) )
420  {
421  for ( cond=pref->inst->top_of_instantiated_conditions; cond!=NIL; cond=cond->next )
422  {
423  if ( ( cond->type == POSITIVE_CONDITION ) && ( cond->bt.wme_->wma_tc_value != tc ) )
424  {
425  cond_wme = cond->bt.wme_;
426  cond_wme->wma_tc_value = tc;
427 
428  if ( cond_wme->wma_decay_el )
429  {
430  if ( !cond_wme->wma_decay_el->just_created )
431  {
432  num_cond_wmes++;
433  combined_time_sum += wma_get_wme_activation( my_agent, cond_wme, false );
434  }
435  }
436  else if ( cond_wme->preference )
437  {
438  if ( cond_wme->preference->wma_o_set )
439  {
440  for ( wme_p=cond_wme->preference->wma_o_set->begin(); wme_p!=cond_wme->preference->wma_o_set->end(); wme_p++ )
441  {
442  if ( ( (*wme_p)->wma_tc_value != tc ) && ( !(*wme_p)->wma_decay_el || !(*wme_p)->wma_decay_el->just_created ) )
443  {
444  num_cond_wmes++;
445  combined_time_sum += wma_get_wme_activation( my_agent, (*wme_p), false );
446 
447  (*wme_p)->wma_tc_value = tc;
448  }
449  }
450  }
451  }
452  else
453  {
454  num_cond_wmes++;
455  combined_time_sum += wma_get_wme_activation( my_agent, cond_wme, false );
456  }
457  }
458  }
459  }
460  }
461 
462  if ( num_cond_wmes )
463  {
464  return_val = static_cast<wma_reference>( floor( combined_time_sum / num_cond_wmes ) );
465  }
466 
467  return return_val;
468 }
469 
470 void wma_activate_wme( agent* my_agent, wme* w, wma_reference num_references, wma_pooled_wme_set* o_set, bool o_only )
471 {
472  // o-supported, non-architectural WME
474  {
475  wma_decay_element* temp_el = w->wma_decay_el;
476 
477  // if decay structure doesn't exist, create it
478  if ( !temp_el )
479  {
480  allocate_with_pool( my_agent, &( my_agent->wma_decay_element_pool ), &temp_el );
481 
482  temp_el->this_wme = w;
483  temp_el->just_removed = false;
484 
485  temp_el->just_created = true;
486  temp_el->num_references = wma_calculate_initial_boost( my_agent, w );
487 
488  temp_el->touches.history_ct = 0;
489  temp_el->touches.next_p = 0;
490 
491  for ( int i=0; i<WMA_DECAY_HISTORY; i++ )
492  {
493  temp_el->touches.access_history[ i ].d_cycle = 0;
494  temp_el->touches.access_history[ i ].num_references = 0;
495  }
496 
497  temp_el->touches.history_references = 0;
498  temp_el->touches.total_references = 0;
499  temp_el->touches.first_reference = 0;
500 
501  // prevents confusion with delayed forgetting
502  temp_el->forget_cycle = static_cast< wma_d_cycle >( -1 );
503 
504  w->wma_decay_el = temp_el;
505 
506  if ( my_agent->sysparams[ TRACE_WMA_SYSPARAM ] )
507  {
508  std::string msg( "WMA @" );
509  std::string temp;
510 
511  to_string( my_agent->d_cycle_count, temp );
512  msg.append( temp );
513  msg.append( ": " );
514 
515  msg.append( "add " );
516 
517  to_string( w->timetag, temp );
518  msg.append( temp );
519  msg.append( " " );
520 
521  to_string( w->id->id.name_letter, temp );
522  msg.append( temp );
523 
524  to_string( w->id->id.name_number, temp );
525  msg.append( temp );
526  msg.append( " " );
527 
528  switch ( w->attr->common.symbol_type )
529  {
531  to_string( w->attr->ic.value, temp );
532  break;
533 
535  to_string( w->attr->fc.value, temp );
536  break;
537 
539  to_string( w->attr->sc.name, temp );
540  break;
541  }
542 
543  msg.append( temp );
544  msg.append( " " );
545 
546  switch ( w->value->common.symbol_type )
547  {
549  to_string( w->value->ic.value, temp );
550  break;
551 
553  to_string( w->value->fc.value, temp );
554  break;
555 
557  to_string( w->value->sc.name, temp );
558  break;
559  }
560 
561  msg.append( temp );
562  msg.append( "\n" );
563 
564  print( my_agent, msg.c_str() );
565  xml_generate_warning( my_agent, msg.c_str() );
566  }
567  }
568 
569  // add to o_set if necessary
570  if ( o_set )
571  {
572  o_set->insert( w );
573  }
574  // otherwise update the decay element
575  else
576  {
577  temp_el->num_references += num_references;
578  my_agent->wma_touched_elements->insert( w );
579  }
580  }
581  // i-supported, non-architectural WME
582  else if ( !o_only && ( w->preference ) && ( w->preference->reference_count ) )
583  {
584  wma_pooled_wme_set* my_o_set = w->preference->wma_o_set;
585  wma_pooled_wme_set::iterator wme_p;
586 
587  // if doesn't have an o_set, populate
588  if ( !my_o_set )
589  {
590  allocate_with_pool( my_agent, &( my_agent->wma_wme_oset_pool ), &my_o_set );
591 #ifdef USE_MEM_POOL_ALLOCATORS
592  my_o_set = new( my_o_set ) wma_pooled_wme_set( std::less< wme* >(), soar_module::soar_memory_pool_allocator< wme* >( my_agent ) );
593 #else
594  my_o_set = new( my_o_set ) wma_pooled_wme_set();
595 #endif
596 
597  w->preference->wma_o_set = my_o_set;
598 
600  {
601  if ( c->type == POSITIVE_CONDITION )
602  {
603  wma_activate_wme( my_agent, c->bt.wme_, 0, my_o_set );
604  }
605  }
606 
607  for ( wme_p=my_o_set->begin(); wme_p!=my_o_set->end(); wme_p++ )
608  {
609  // add a ref to wmes on this list
610  wme_add_ref( (*wme_p) );
611  }
612  }
613 
614  // iterate over the o_set
615  for ( wme_p=my_o_set->begin(); wme_p!=my_o_set->end(); wme_p++ )
616  {
617  // if populating o_set, add
618  if ( o_set )
619  {
620  o_set->insert( (*wme_p) );
621  }
622  // otherwise, "activate" the wme if it is
623  // non-architectural (avoids dereferencing
624  // the wme preference)
625  else
626  {
627  if ( (*wme_p)->wma_decay_el )
628  {
629  (*wme_p)->wma_decay_el->num_references += num_references;
630  my_agent->wma_touched_elements->insert( (*wme_p) );
631  }
632  }
633  }
634  }
635  // architectural
636  else if ( !o_only && !w->preference )
637  {
638  // only action is to add it to the o_set
639  if ( o_set )
640  {
641  o_set->insert( w );
642  }
643  }
644 }
645 
646 inline void wma_forgetting_remove_from_p_queue( agent* my_agent, wma_decay_element* decay_el );
647 void wma_deactivate_element( agent* my_agent, wme* w )
648 {
649  wma_decay_element* temp_el = w->wma_decay_el;
650 
651  if ( temp_el )
652  {
653  if ( !temp_el->just_removed )
654  {
655  my_agent->wma_touched_elements->erase( w );
656 
658  {
659  wma_forgetting_remove_from_p_queue( my_agent, temp_el );
660  }
661 
662  temp_el->just_removed = true;
663  }
664  }
665 }
666 
667 void wma_remove_decay_element( agent* my_agent, wme* w )
668 {
669  wma_decay_element* temp_el = w->wma_decay_el;
670 
671  if ( temp_el )
672  {
673  // Deactivate the wme first
674  if ( !temp_el->just_removed )
675  {
676  wma_deactivate_element( my_agent, w );
677  }
678 
679  // log
680  if ( my_agent->sysparams[ TRACE_WMA_SYSPARAM ] )
681  {
682  std::string msg( "WMA @" );
683  std::string temp;
684 
685  to_string( my_agent->d_cycle_count, temp );
686  msg.append( temp );
687  msg.append( ": " );
688 
689  msg.append( "remove " );
690 
691  to_string( w->timetag, temp );
692  msg.append( temp );
693 
694  msg.append( "\n" );
695 
696  print( my_agent, msg.c_str() );
697  xml_generate_warning( my_agent, msg.c_str() );
698  }
699 
700  free_with_pool( &( my_agent->wma_decay_element_pool ), temp_el );
701  w->wma_decay_el = NULL;
702  }
703 }
704 
705 void wma_remove_pref_o_set( agent* my_agent, preference* pref )
706 {
707  if ( pref && pref->wma_o_set )
708  {
709  wma_pooled_wme_set* victim = pref->wma_o_set;
710  pref->wma_o_set = NULL;
711 
712  for ( wma_pooled_wme_set::iterator p=victim->begin(); p!=victim->end(); p++ )
713  {
714  wme_remove_ref( my_agent, (*p) );
715  }
716 
717  victim->~wma_pooled_wme_set();
718  free_with_pool( &( my_agent->wma_wme_oset_pool ), victim );
719  }
720 }
721 
724 
725 
728 // Forgetting Functions (wma::forget)
731 
732 inline void wma_forgetting_add_to_p_queue( agent* my_agent, wma_decay_element* decay_el, wma_d_cycle new_cycle )
733 {
734  if ( decay_el )
735  {
736  decay_el->forget_cycle = new_cycle;
737 
738  wma_forget_p_queue::iterator pq_p = my_agent->wma_forget_pq->find( new_cycle );
739  if ( pq_p == my_agent->wma_forget_pq->end() )
740  {
741  wma_decay_set* newbie;
742  allocate_with_pool( my_agent, &( my_agent->wma_decay_set_pool ), &newbie );
743 #ifdef USE_MEM_POOL_ALLOCATORS
744  newbie = new (newbie) wma_decay_set( std::less< wma_decay_element* >(), soar_module::soar_memory_pool_allocator< wma_decay_element* >( my_agent ) );
745 #else
746  newbie = new (newbie) wma_decay_set();
747 #endif
748  newbie->insert( decay_el );
749 
750  my_agent->wma_forget_pq->insert( std::make_pair< wma_d_cycle, wma_decay_set* >( new_cycle, newbie ) );
751  }
752  else
753  {
754  pq_p->second->insert( decay_el );
755  }
756  }
757 }
758 
759 inline void wma_forgetting_remove_from_p_queue( agent* my_agent, wma_decay_element* decay_el )
760 {
761  if ( decay_el )
762  {
763  // try to find set for the element per cycle
764  wma_forget_p_queue::iterator pq_p = my_agent->wma_forget_pq->find( decay_el->forget_cycle );
765  if ( pq_p != my_agent->wma_forget_pq->end() )
766  {
767  wma_decay_set::iterator d_p = pq_p->second->find( decay_el );
768  if ( d_p != pq_p->second->end() )
769  {
770  pq_p->second->erase( d_p );
771 
772  if ( pq_p->second->empty() )
773  {
774  my_agent->wma_touched_sets->insert( pq_p->first );
775  }
776  }
777  }
778  }
779 }
780 
781 inline void wma_forgetting_move_in_p_queue( agent* my_agent, wma_decay_element* decay_el, wma_d_cycle new_cycle )
782 {
783  if ( decay_el && ( decay_el->forget_cycle != new_cycle ) )
784  {
785  wma_forgetting_remove_from_p_queue( my_agent, decay_el );
786  wma_forgetting_add_to_p_queue( my_agent, decay_el, new_cycle );
787  }
788 }
789 
790 inline wma_d_cycle wma_forgetting_estimate_cycle( agent* my_agent, wma_decay_element* decay_el, bool fresh_reference )
791 {
792  wma_d_cycle return_val = static_cast<wma_d_cycle>( my_agent->wma_d_cycle_count );
794 
795  if ( fresh_reference && ( forgetting == wma_param_container::approx ) )
796  {
797  wma_d_cycle to_add = 0;
798 
799  wma_history* history = &( decay_el->touches );
800  unsigned int p = history->next_p;
801  unsigned int counter = history->history_ct;
802  wma_d_cycle cycle_diff = 0;
803  wma_reference approx_ref;
804 
805  //
806 
807  while ( counter )
808  {
809  p = wma_history_prev( p );
810 
811  cycle_diff = ( return_val - history->access_history[ p ].d_cycle );
812 
813  approx_ref = ( ( history->access_history[ p ].num_references < WMA_REFERENCES_PER_DECISION )?( history->access_history[ p ].num_references ):( WMA_REFERENCES_PER_DECISION-1 ) );
814  if ( my_agent->wma_approx_array[ approx_ref ] > cycle_diff )
815  {
816  to_add += ( my_agent->wma_approx_array[ approx_ref ] - cycle_diff );
817  }
818 
819  counter--;
820  }
821 
822  return_val += to_add;
823  }
824 
825  if ( return_val == static_cast<wma_d_cycle>( my_agent->wma_d_cycle_count ) )
826  {
827  double my_thresh = my_agent->wma_thresh_exp;
828 
829  // binary parameter search
830  {
831  wma_d_cycle to_add = 1;
832  double act = wma_calculate_decay_activation( my_agent, decay_el, ( return_val + to_add ), false );
833 
834  if ( act >= my_thresh )
835  {
836  while ( act >= my_thresh )
837  {
838  to_add *= 2;
839  act = wma_calculate_decay_activation( my_agent, decay_el, ( return_val + to_add ), false );
840  }
841 
842  //
843 
844  wma_d_cycle upper_bound = to_add;
845  wma_d_cycle lower_bound, mid;
846  if ( to_add < 4 )
847  {
848  lower_bound = upper_bound;
849  }
850  else
851  {
852  lower_bound = ( to_add / 2 );
853  }
854 
855  while ( lower_bound != upper_bound )
856  {
857  mid = ( ( lower_bound + upper_bound ) / 2 );
858  act = wma_calculate_decay_activation( my_agent, decay_el, ( return_val + mid ), false );
859 
860  if ( act < my_thresh )
861  {
862  upper_bound = mid;
863 
864  if ( upper_bound - lower_bound <= 1 )
865  {
866  lower_bound = mid;
867  }
868  }
869  else
870  {
871  lower_bound = mid;
872 
873  if ( upper_bound - lower_bound <= 1 )
874  {
875  lower_bound = upper_bound;
876  }
877  }
878  }
879 
880  to_add = upper_bound;
881  }
882 
883  return_val += to_add;
884  }
885  }
886 
887  return return_val;
888 }
889 
890 inline bool wma_forgetting_forget_wme( agent *my_agent, wme *w )
891 {
892  bool return_val = false;
893  bool fake = ( my_agent->wma_params->fake_forgetting->get_value() == soar_module::on );
894 
895  if ( w->preference && w->preference->slot )
896  {
898  preference* next_p;
899 
900  while ( p )
901  {
902  next_p = p->all_of_slot_next;
903 
904  if ( p->o_supported && p->in_tm && ( p->value == w->value ) )
905  {
906  if ( !fake )
907  {
908  remove_preference_from_tm( my_agent, p );
909  return_val = true;
910  }
911  }
912 
913  p = next_p;
914  }
915  }
916 
917  return return_val;
918 }
919 
920 inline bool wma_forgetting_update_p_queue( agent* my_agent )
921 {
922  bool return_val = false;
923  bool do_forget = false;
924  slot* s;
925  wme* w;
926 
927  if ( !my_agent->wma_forget_pq->empty() )
928  {
929  wma_forget_p_queue::iterator pq_p = my_agent->wma_forget_pq->begin();
930  wma_d_cycle current_cycle = my_agent->wma_d_cycle_count;
931  double decay_thresh = my_agent->wma_thresh_exp;
932  bool forget_only_lti = ( my_agent->wma_params->forget_wme->get_value() == wma_param_container::lti );
933 
934  if ( pq_p->first == current_cycle )
935  {
936  wma_decay_set::iterator d_p=pq_p->second->begin();
937  wma_decay_set::iterator current_p;
938 
939  while ( d_p != pq_p->second->end() )
940  {
941  current_p = d_p++;
942 
943  if ( wma_calculate_decay_activation( my_agent, (*current_p), current_cycle, false ) < decay_thresh )
944  {
945  (*current_p)->forget_cycle = WMA_FORGOTTEN_CYCLE;
946 
947  if ( !forget_only_lti || ( (*current_p)->this_wme->id->id.smem_lti != NIL ) )
948  {
949  do_forget = true;
950 
951  // implements all-or-nothing check for lti mode
952  if ( forget_only_lti )
953  {
954  for ( s=(*current_p)->this_wme->id->id.slots; (s && do_forget); s=s->next )
955  {
956  for ( w=s->wmes; (w && do_forget); w=w->next )
957  {
959  {
960  do_forget = false;
961  }
962  }
963  }
964  }
965 
966  if ( do_forget )
967  {
968  if ( forget_only_lti )
969  {
970  // implements all-or-nothing forget for lti mode
971  for ( s=(*current_p)->this_wme->id->id.slots; (s && do_forget); s=s->next )
972  {
973  for ( w=s->wmes; (w && do_forget); w=w->next )
974  {
975  if ( wma_forgetting_forget_wme( my_agent, w ) )
976  {
977  return_val = true;
978  }
979  }
980  }
981  }
982  else
983  {
984  if ( wma_forgetting_forget_wme( my_agent, (*current_p)->this_wme ) )
985  {
986  return_val = true;
987  }
988  }
989  }
990  }
991  }
992  else
993  {
994  wma_forgetting_move_in_p_queue( my_agent, (*current_p), wma_forgetting_estimate_cycle( my_agent, (*current_p), false ) );
995  }
996  }
997 
998  // clean up decay set
999  my_agent->wma_touched_sets->insert( pq_p->first );
1000  pq_p->second->clear();
1001  }
1002 
1003  // clean up touched sets
1004  for ( wma_decay_cycle_set::iterator touched_it=my_agent->wma_touched_sets->begin(); touched_it!=my_agent->wma_touched_sets->end(); touched_it++ )
1005  {
1006  pq_p = my_agent->wma_forget_pq->find( *touched_it );
1007 
1008  if ( ( pq_p != my_agent->wma_forget_pq->end() ) && ( pq_p->second->empty() ) )
1009  {
1010  pq_p->second->~wma_decay_set();
1011  free_with_pool( &( my_agent->wma_decay_set_pool ), pq_p->second );
1012 
1013  my_agent->wma_forget_pq->erase( pq_p );
1014  }
1015  }
1016  my_agent->wma_touched_sets->clear();
1017  }
1018 
1019  return return_val;
1020 }
1021 
1022 inline bool wma_forgetting_naive_sweep( agent* my_agent )
1023 {
1024  wma_d_cycle current_cycle = my_agent->wma_d_cycle_count;
1025  double decay_thresh = my_agent->wma_thresh_exp;
1026  bool forget_only_lti = ( my_agent->wma_params->forget_wme->get_value() == wma_param_container::lti );
1027  bool return_val = false;
1028 
1029  for ( wme* w=my_agent->all_wmes_in_rete; w; w=w->rete_next )
1030  {
1031  if ( w->wma_decay_el && ( !forget_only_lti || ( w->id->id.smem_lti != NIL ) ) )
1032  {
1033  // to be forgotten, wme must...
1034  // - have been accessed (can't imagine why not, but just in case)
1035  // - not have been accessed this cycle (i.e. no decay)
1036  // - have activation less than threshold
1037  if ( ( w->wma_decay_el->touches.total_references>0 ) &&
1038  ( w->wma_decay_el->touches.access_history[ wma_history_prev( w->wma_decay_el->touches.next_p ) ].d_cycle < current_cycle ) &&
1039  ( wma_calculate_decay_activation( my_agent, w->wma_decay_el, current_cycle, false ) < decay_thresh ) )
1040  {
1041  if ( wma_forgetting_forget_wme( my_agent, w ) )
1042  {
1043  return_val = true;
1044  }
1045  }
1046  }
1047  }
1048 
1049  return return_val;
1050 }
1051 
1054 
1055 
1058 // Activation Update Functions (wma::update)
1061 
1063 {
1064  wme* w;
1065 
1066  if ( pref->type == ACCEPTABLE_PREFERENCE_TYPE )
1067  {
1068  w = pref->slot->wmes;
1069  while ( w )
1070  {
1071  // id and attr should already match so just compare the value
1072  if ( w->value == pref->value )
1073  {
1074  wma_activate_wme( my_agent, w );
1075  }
1076 
1077  w = w->next;
1078  }
1079  }
1080 }
1081 
1083 {
1084  ms_change *msc;
1085  token temp_token, *t;
1086 
1087  for ( msc=my_agent->ms_o_assertions; msc!=NIL; msc=msc->next )
1088  {
1089  temp_token.parent = msc->tok;
1090  temp_token.w = msc->w;
1091  t = &temp_token;
1092 
1093  while ( t != my_agent->dummy_top_token )
1094  {
1095  if (t->w != NIL)
1096  {
1097  wma_activate_wme( my_agent, t->w );
1098  }
1099 
1100  t = t->parent;
1101  }
1102  }
1103 
1104  for ( msc=my_agent->ms_i_assertions; msc!=NIL; msc=msc->next )
1105  {
1106  temp_token.parent = msc->tok;
1107  temp_token.w = msc->w;
1108  t = &temp_token;
1109 
1110  while ( t != my_agent->dummy_top_token )
1111  {
1112  if ( t->w != NIL )
1113  {
1114  wma_activate_wme( my_agent, t->w );
1115  }
1116 
1117  t = t->parent;
1118  }
1119  }
1120 }
1121 
1122 inline void wma_update_decay_histories( agent* my_agent )
1123 {
1124  wma_pooled_wme_set::iterator wme_p;
1125  wma_decay_element* temp_el;
1126  wma_d_cycle current_cycle = my_agent->wma_d_cycle_count;
1127  bool forgetting = ( ( my_agent->wma_params->forgetting->get_value() == wma_param_container::approx ) || ( my_agent->wma_params->forgetting->get_value() == wma_param_container::bsearch ) );
1128 
1129  // add to history for changed elements
1130  for ( wme_p=my_agent->wma_touched_elements->begin(); wme_p!=my_agent->wma_touched_elements->end(); wme_p++ )
1131  {
1132  temp_el = (*wme_p)->wma_decay_el;
1133 
1134  // update number of references in the current history
1135  // (has to come before history overwrite)
1136  temp_el->touches.history_references += ( temp_el->num_references - temp_el->touches.access_history[ temp_el->touches.next_p ].num_references );
1137 
1138  // set history
1139  temp_el->touches.access_history[ temp_el->touches.next_p ].d_cycle = current_cycle;
1140  temp_el->touches.access_history[ temp_el->touches.next_p ].num_references = temp_el->num_references;
1141 
1142  // log
1143  if ( my_agent->sysparams[ TRACE_WMA_SYSPARAM ] )
1144  {
1145  std::string msg( "WMA @" );
1146  std::string temp;
1147 
1148  to_string( my_agent->d_cycle_count, temp );
1149  msg.append( temp );
1150  msg.append( ": " );
1151 
1152  msg.append( "activate " );
1153 
1154  to_string( temp_el->this_wme->timetag, temp );
1155  msg.append( temp );
1156  msg.append( " " );
1157 
1158  to_string( temp_el->num_references, temp );
1159  msg.append( temp );
1160 
1161  msg.append( "\n" );
1162 
1163  print( my_agent, msg.c_str() );
1164  xml_generate_warning( my_agent, msg.c_str() );
1165  }
1166 
1167  // keep track of first reference
1168  if ( temp_el->touches.total_references == 0 )
1169  {
1170  temp_el->touches.first_reference = current_cycle;
1171  }
1172 
1173  // update counters
1174  if ( temp_el->touches.history_ct < WMA_DECAY_HISTORY )
1175  {
1176  temp_el->touches.history_ct++;
1177  }
1178  temp_el->touches.next_p = wma_history_next( temp_el->touches.next_p );
1179  temp_el->touches.total_references += temp_el->num_references;
1180 
1181  // reset cycle counter
1182  temp_el->num_references = 0;
1183 
1184  // update forgetting stuff as needed
1185  if ( forgetting )
1186  {
1187  if ( temp_el->just_created )
1188  {
1189  wma_forgetting_add_to_p_queue( my_agent, temp_el, wma_forgetting_estimate_cycle( my_agent, temp_el, true ) );
1190  }
1191  else
1192  {
1193  wma_forgetting_move_in_p_queue( my_agent, temp_el, wma_forgetting_estimate_cycle( my_agent, temp_el, true ) );
1194  }
1195  }
1196 
1197  temp_el->just_created = false;
1198  }
1199  my_agent->wma_touched_elements->clear();
1200 }
1201 
1204 
1205 
1208 // API Functions (wma::api)
1211 
1212 double wma_get_wme_activation( agent* my_agent, wme* w, bool log_result )
1213 {
1214  double return_val = static_cast<double>( ( log_result )?( WMA_ACTIVATION_NONE ):( WMA_TIME_SUM_NONE ) );
1215 
1216  if ( w->wma_decay_el )
1217  {
1218  return_val = wma_calculate_decay_activation( my_agent, w->wma_decay_el, my_agent->wma_d_cycle_count, log_result );
1219  }
1220 
1221  return return_val;
1222 }
1223 
1224 inline void _wma_ref_to_str( wma_cycle_reference& ref, wma_d_cycle current_cycle, std::string& str )
1225 {
1226  std::string temp;
1227  wma_d_cycle cycle_diff = ( current_cycle - ref.d_cycle );
1228 
1229  to_string( ref.num_references, temp );
1230  str.append( temp );
1231 
1232  str.append( " @ d" );
1233 
1234  to_string( ref.d_cycle, temp );
1235  str.append( temp );
1236 
1237  str.append( " (-" );
1238 
1239  to_string( cycle_diff, temp );
1240  str.append( temp );
1241 
1242  str.append( ")" );
1243 }
1244 
1245 void wma_get_wme_history( agent* my_agent, wme* w, std::string& buffer )
1246 {
1247  if ( w->wma_decay_el )
1248  {
1249  wma_history* history = &( w->wma_decay_el->touches );
1250  unsigned int p = history->next_p;
1251  unsigned int counter = history->history_ct;
1252  wma_d_cycle current_cycle = my_agent->wma_d_cycle_count;
1253 
1254  //
1255 
1256  buffer.append( "history (" );
1257 
1258  {
1259  std::string temp;
1260 
1261  to_string( history->history_references, temp );
1262  buffer.append( temp );
1263 
1264  buffer.append( "/" );
1265 
1266  to_string( history->total_references, temp );
1267  buffer.append( temp );
1268 
1269  buffer.append( ", first @ d" );
1270 
1271  to_string( history->first_reference, temp );
1272  buffer.append( temp );
1273  }
1274 
1275  buffer.append( "):" );
1276 
1277  //
1278 
1279  while ( counter )
1280  {
1281  p = wma_history_prev( p );
1282  counter--;
1283 
1284  buffer.append( "\n " );
1285  _wma_ref_to_str( history->access_history[ p ], current_cycle, buffer );
1286  }
1287 
1288  //
1289 
1291 
1292  if ( ( forget == wma_param_container::bsearch ) || ( forget == wma_param_container::approx ) )
1293  {
1294  buffer.append( "\n\n" );
1295  buffer.append( "considering WME for decay @ d" );
1296 
1297  std::string temp;
1298  to_string( w->wma_decay_el->forget_cycle, temp );
1299  buffer.append( temp );
1300  }
1301  }
1302  else
1303  {
1304  buffer.assign( "WME has no decay history" );
1305  }
1306 }
1307 
1308 void wma_go( agent* my_agent, wma_go_action go_action )
1309 {
1310  // update history for all touched elements
1311  if ( go_action == wma_histories )
1312  {
1313  my_agent->wma_timers->history->start();
1314 
1315  wma_update_decay_histories( my_agent );
1316 
1317  my_agent->wma_timers->history->stop();
1318  }
1319  // check forgetting queue
1320  else if ( go_action == wma_forgetting )
1321  {
1323 
1324  if ( forgetting != wma_param_container::off )
1325  {
1326  my_agent->wma_timers->forgetting->start();
1327 
1328  bool forgot_something = false;
1329 
1330  if ( forgetting == wma_param_container::naive )
1331  {
1332  forgot_something = wma_forgetting_naive_sweep( my_agent );
1333  }
1334  else
1335  {
1336  forgot_something = wma_forgetting_update_p_queue( my_agent );
1337  }
1338 
1339  if ( forgot_something )
1340  {
1341  if ( my_agent->sysparams[ TRACE_WM_CHANGES_SYSPARAM ] )
1342  {
1343  const char *msg = "\n\nWMA: BEGIN FORGOTTEN WME LIST\n\n";
1344 
1345  print( my_agent, const_cast<char *>( msg ) );
1346  xml_generate_message( my_agent, const_cast<char *>( msg ) );
1347  }
1348 
1349  uint64_t wm_removal_diff = my_agent->wme_removal_count;
1350  {
1351  do_working_memory_phase( my_agent );
1352  }
1353  wm_removal_diff = ( my_agent->wme_removal_count - wm_removal_diff );
1354 
1355  if ( wm_removal_diff > 0 )
1356  {
1357  my_agent->wma_stats->forgotten_wmes->set_value( my_agent->wma_stats->forgotten_wmes->get_value() + static_cast< int64_t >( wm_removal_diff ) );
1358  }
1359 
1360  if ( my_agent->sysparams[ TRACE_WM_CHANGES_SYSPARAM ] )
1361  {
1362  const char *msg = "\nWMA: END FORGOTTEN WME LIST\n\n";
1363 
1364  print( my_agent, const_cast<char *>( msg ) );
1365  xml_generate_message( my_agent, const_cast<char *>( msg ) );
1366  }
1367  }
1368 
1369  my_agent->wma_timers->forgetting->stop();
1370  }
1371  }
1372 }
1373