Soar Kernel  9.3.2 08-06-12
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
episodic_memory.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: episodic_memory.cpp
11  *
12  * =======================================================================
13  * Description : Various functions for Soar-EpMem
14  * =======================================================================
15  */
16 
17 #include <cmath>
18 #include <algorithm>
19 #include <iterator>
20 #include <iostream>
21 #include <fstream>
22 #include <set>
23 #include <climits>
24 
25 #include "episodic_memory.h"
26 #include "semantic_memory.h"
27 
28 #include "agent.h"
29 #include "prefmem.h"
30 #include "symtab.h"
31 #include "wmem.h"
32 #include "print.h"
33 #include "xml.h"
34 #include "instantiations.h"
35 #include "decide.h"
36 
37 #ifdef EPMEM_EXPERIMENT
38 
39 uint64_t epmem_episodes_searched = 0;
40 uint64_t epmem_dc_interval_inserts = 0;
41 uint64_t epmem_dc_interval_removes = 0;
42 uint64_t epmem_dc_wme_adds = 0;
43 std::ofstream* epmem_exp_output = NULL;
44 
45 enum epmem_exp_states
46 {
47  exp_state_wm_adds,
48  exp_state_wm_removes,
49  exp_state_sqlite_mem,
50 };
51 
52 int64_t epmem_exp_state[] = { 0, 0, 0 };
53 
54 soar_module::timer* epmem_exp_timer = NULL;
55 
56 #endif
57 
60 // Bookmark strings to help navigate the code
63 
64 // parameters epmem::param
65 // stats epmem::stats
66 // timers epmem::timers
67 // statements epmem::statements
68 
69 // wme-related epmem::wmes
70 
71 // variable abstraction epmem::var
72 
73 // relational interval tree epmem::rit
74 
75 // cleaning up epmem::clean
76 // initialization epmem::init
77 
78 // temporal hash epmem::hash
79 
80 // storing new episodes epmem::storage
81 // non-cue-based queries epmem::ncb
82 // cue-based queries epmem::cbr
83 
84 // vizualization epmem::viz
85 
86 // high-level api epmem::api
87 
88 
91 // Parameter Functions (epmem::params)
94 
95 epmem_param_container::epmem_param_container( agent *new_agent ): soar_module::param_container( new_agent )
96 {
97  // learning
99  add( learning );
100 
102  // Encoding
104 
105  // phase
107  phase->add_mapping( phase_output, "output" );
108  phase->add_mapping( phase_selection, "selection" );
109  add( phase );
110 
111  // trigger
113  trigger->add_mapping( none, "none" );
114  trigger->add_mapping( output, "output" );
115  trigger->add_mapping( dc, "dc" );
116  add( trigger );
117 
118  // force
120  force->add_mapping( remember, "remember" );
121  force->add_mapping( ignore, "ignore" );
122  force->add_mapping( force_off, "off" );
123  add( force );
124 
125  // exclusions - this is initialized with "epmem" directly after hash tables
127  add( exclusions );
128 
129 
131  // Storage
133 
134  // database
136  database->add_mapping( memory, "memory" );
137  database->add_mapping( file, "file" );
138  add( database );
139 
140  // path
142  add( path );
143 
144  // auto-commit
146  add( lazy_commit );
147 
148 
150  // Retrieval
152 
153  // graph-match
155  add( graph_match );
156 
157  // balance
159  add( balance );
160 
161 
163  // Performance
165 
166  // timers
168  timers->add_mapping( soar_module::timer::zero, "off" );
172  add( timers );
173 
174  // page_size
176  page_size->add_mapping( page_1k, "1k" );
177  page_size->add_mapping( page_2k, "2k" );
178  page_size->add_mapping( page_4k, "4k" );
179  page_size->add_mapping( page_8k, "8k" );
180  page_size->add_mapping( page_16k, "16k" );
181  page_size->add_mapping( page_32k, "32k" );
182  page_size->add_mapping( page_64k, "64k" );
183  add( page_size );
184 
185  // cache_size
187  add( cache_size );
188 
189  // opt
191  opt->add_mapping( opt_safety, "safety" );
192  opt->add_mapping( opt_speed, "performance" );
193  add( opt );
194 
195 
197  // Experimental
199 
201  gm_ordering->add_mapping( gm_order_undefined, "undefined" );
204  add( gm_ordering );
205 
206  // merge
208  merge->add_mapping( merge_none, "none" );
209  merge->add_mapping( merge_add, "add" );
210  add( merge );
211 }
212 
213 //
214 
215 epmem_path_param::epmem_path_param( const char *new_name, const char *new_value, soar_module::predicate<const char *> *new_val_pred, soar_module::predicate<const char *> *new_prot_pred, agent *new_agent ): soar_module::string_param( new_name, new_value, new_val_pred, new_prot_pred ), my_agent( new_agent ) {}
216 
217 void epmem_path_param::set_value( const char *new_value )
218 {
220  {
221  my_agent->epmem_first_switch = false;
223 
224  const char *msg = "Database set to file";
225  print( my_agent, const_cast<char *>( msg ) );
226  xml_generate_message( my_agent, const_cast<char *>( msg ) );
227  }
228 
229  value->assign( new_value );
230 }
231 
232 //
233 
234 template <typename T>
235 epmem_db_predicate<T>::epmem_db_predicate( agent *new_agent ): soar_module::agent_predicate<T>( new_agent ) {}
236 
237 template <typename T>
238 bool epmem_db_predicate<T>::operator() ( T /*val*/ ) { return ( this->my_agent->epmem_db->get_status() == soar_module::connected ); }
239 
240 
241 /***************************************************************************
242  * Function : epmem_enabled
243  * Author : Nate Derbinsky
244  * Notes : Shortcut function to system parameter
245  **************************************************************************/
246 bool epmem_enabled( agent *my_agent )
247 {
248  return ( my_agent->epmem_params->learning->get_value() == soar_module::on );
249 }
250 
253 // Statistic Functions (epmem::stats)
256 
257 epmem_stat_container::epmem_stat_container( agent *new_agent ): soar_module::stat_container( new_agent )
258 {
259  // time
261  add( time );
262 
263  // db-lib-version
265  add( db_lib_version );
266 
267  // mem-usage
269  add( mem_usage );
270 
271  // mem-high
273  add( mem_high );
274 
275  // cue-based-retrievals
277  add( cbr );
278 
279  // nexts
281  add( nexts );
282 
283  // prev's
285  add( prevs );
286 
287  // ncb-wmes
289  add( ncb_wmes );
290 
291  // qry-pos
293  add( qry_pos );
294 
295  // qry-neg
297  add( qry_neg );
298 
299  // qry-ret
301  add( qry_ret );
302 
303  // qry-card
305  add( qry_card );
306 
307  // qry-lits
309  add( qry_lits );
310 
311  // next-id
313  add( next_id );
314 
315  // rit-offset-1
317  add( rit_offset_1 );
318 
319  // rit-left-root-1
321  add( rit_left_root_1 );
322 
323  // rit-right-root-1
326 
327  // rit-min-step-1
329  add( rit_min_step_1 );
330 
331  // rit-offset-2
333  add( rit_offset_2 );
334 
335  // rit-left-root-2
337  add( rit_left_root_2 );
338 
339  // rit-right-root-2
342 
343  // rit-min-step-2
345  add( rit_min_step_2 );
346 
347 
349  // connect to rit state
351 
352  // graph
361 
370 }
371 
372 //
373 
374 epmem_db_lib_version_stat::epmem_db_lib_version_stat( agent* new_agent, const char* new_name, const char* new_value, soar_module::predicate< const char* >* new_prot_pred ): soar_module::primitive_stat< const char* >( new_name, new_value, new_prot_pred ), my_agent( new_agent ) {}
375 
377 {
378  return my_agent->epmem_db->lib_version();
379 }
380 
381 //
382 
383 epmem_mem_usage_stat::epmem_mem_usage_stat( agent *new_agent, const char *new_name, int64_t new_value, soar_module::predicate<int64_t> *new_prot_pred ): soar_module::integer_stat( new_name, new_value, new_prot_pred ), my_agent( new_agent ) {}
384 
386 {
387  return my_agent->epmem_db->memory_usage();
388 }
389 
390 //
391 
392 epmem_mem_high_stat::epmem_mem_high_stat( agent *new_agent, const char *new_name, int64_t new_value, soar_module::predicate<int64_t> *new_prot_pred ): soar_module::integer_stat( new_name, new_value, new_prot_pred ), my_agent( new_agent ) {}
393 
395 {
397 }
398 
399 
402 // Timer Functions (epmem::timers)
405 
406 epmem_timer_container::epmem_timer_container( agent *new_agent ): soar_module::timer_container( new_agent )
407 {
408  // one
409 
411  add( total );
412 
413  // two
414 
415  storage = new epmem_timer( "epmem_storage", my_agent, soar_module::timer::two );
416  add( storage );
417 
418  ncb_retrieval = new epmem_timer( "epmem_ncb_retrieval", my_agent, soar_module::timer::two );
419  add( ncb_retrieval );
420 
421  query = new epmem_timer( "epmem_query", my_agent, soar_module::timer::two );
422  add( query );
423 
424  api = new epmem_timer( "epmem_api", my_agent, soar_module::timer::two );
425  add( api );
426 
427  trigger = new epmem_timer( "epmem_trigger", my_agent, soar_module::timer::two );
428  add( trigger );
429 
430  init = new epmem_timer( "epmem_init", my_agent, soar_module::timer::two );
431  add( init );
432 
433  next = new epmem_timer( "epmem_next", my_agent, soar_module::timer::two );
434  add( next );
435 
436  prev = new epmem_timer( "epmem_prev", my_agent, soar_module::timer::two );
437  add( prev );
438 
439  hash = new epmem_timer( "epmem_hash", my_agent, soar_module::timer::two );
440  add( hash );
441 
442  wm_phase = new epmem_timer( "epmem_wm_phase", my_agent, soar_module::timer::two );
443  add( wm_phase );
444 
445  // three
446 
448  add( ncb_edge );
449 
451  add( ncb_edge_rit );
452 
454  add( ncb_node );
455 
457  add( ncb_node_rit );
458 
460  add( query_dnf );
461 
463  add( query_walk );
464 
465  query_walk_edge = new epmem_timer( "query_walk_edge", my_agent, soar_module::timer::three );
466  add( query_walk_edge );
467 
468  query_walk_interval = new epmem_timer( "query_walk_interval", my_agent, soar_module::timer::three );
470 
471  query_graph_match = new epmem_timer( "query_graph_match", my_agent, soar_module::timer::three );
473 
475  add( query_result );
476 
477  query_cleanup = new epmem_timer( "query_cleanup", my_agent, soar_module::timer::three );
478  add( query_cleanup );
479 
480  query_sql_edge = new epmem_timer( "query_sql_edge", my_agent, soar_module::timer::three );
481  add( query_sql_edge );
482 
483  query_sql_start_ep = new epmem_timer( "query_sql_start_ep", my_agent, soar_module::timer::three );
485 
486  query_sql_start_now = new epmem_timer( "query_sql_start_now", my_agent, soar_module::timer::three );
488 
489  query_sql_start_point = new epmem_timer( "query_sql_start_point", my_agent, soar_module::timer::three );
491 
492  query_sql_end_ep = new epmem_timer( "query_sql_end_ep", my_agent, soar_module::timer::three );
494 
495  query_sql_end_now = new epmem_timer( "query_sql_end_now", my_agent, soar_module::timer::three );
497 
498  query_sql_end_point = new epmem_timer( "query_sql_end_point", my_agent, soar_module::timer::three );
500 
502  // connect to rit state
504 
505  // graph
508 }
509 
510 //
511 
512 epmem_timer_level_predicate::epmem_timer_level_predicate( agent *new_agent ): soar_module::agent_predicate<soar_module::timer::timer_level>( new_agent ) {}
513 
515 
516 //
517 
518 epmem_timer::epmem_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 epmem_timer_level_predicate( new_agent ) ) {}
519 
520 
523 // Statement Functions (epmem::statements)
526 
527 epmem_common_statement_container::epmem_common_statement_container( agent *new_agent ): soar_module::sqlite_statement_container( new_agent->epmem_db )
528 {
529  soar_module::sqlite_database *new_db = new_agent->epmem_db;
530 
531  //
532 
533  add_structure( "CREATE TABLE IF NOT EXISTS vars (id INTEGER PRIMARY KEY,value NONE)" );
534  add_structure( "CREATE TABLE IF NOT EXISTS rit_left_nodes (min INTEGER, max INTEGER)" );
535  add_structure( "CREATE TABLE IF NOT EXISTS rit_right_nodes (node INTEGER)" );
536  add_structure( "CREATE TABLE IF NOT EXISTS temporal_symbol_hash (id INTEGER PRIMARY KEY, sym_const NONE, sym_type INTEGER)" );
537  add_structure( "CREATE UNIQUE INDEX IF NOT EXISTS temporal_symbol_hash_const_type ON temporal_symbol_hash (sym_type,sym_const)" );
538 
539  // workaround for tree: type 1 = IDENTIFIER_SYMBOL_TYPE
540  add_structure( "INSERT OR IGNORE INTO temporal_symbol_hash (id,sym_const,sym_type) VALUES (0,NULL,1)" );
541 
542  // workaround for acceptable preference wmes: id 1 = "operator+"
543  add_structure( "INSERT OR IGNORE INTO temporal_symbol_hash (id,sym_const,sym_type) VALUES (1,'operator*',2)" );
544 
545  //
546 
547  begin = new soar_module::sqlite_statement( new_db, "BEGIN" );
548  add( begin );
549 
550  commit = new soar_module::sqlite_statement( new_db, "COMMIT" );
551  add( commit );
552 
553  rollback = new soar_module::sqlite_statement( new_db, "ROLLBACK" );
554  add( rollback );
555 
556  //
557 
558  var_get = new soar_module::sqlite_statement( new_db, "SELECT value FROM vars WHERE id=?" );
559  add( var_get );
560 
561  var_set = new soar_module::sqlite_statement( new_db, "REPLACE INTO vars (id,value) VALUES (?,?)" );
562  add( var_set );
563 
564  //
565 
566  rit_add_left = new soar_module::sqlite_statement( new_db, "INSERT INTO rit_left_nodes (min,max) VALUES (?,?)" );
567  add( rit_add_left );
568 
569  rit_truncate_left = new soar_module::sqlite_statement( new_db, "DELETE FROM rit_left_nodes" );
571 
572  rit_add_right = new soar_module::sqlite_statement( new_db, "INSERT INTO rit_right_nodes (node) VALUES (?)" );
573  add( rit_add_right );
574 
575  rit_truncate_right = new soar_module::sqlite_statement( new_db, "DELETE FROM rit_right_nodes" );
577 
578  //
579 
580  hash_get = new soar_module::sqlite_statement( new_db, "SELECT id FROM temporal_symbol_hash WHERE sym_type=? AND sym_const=?" );
581  add( hash_get );
582 
583  hash_add = new soar_module::sqlite_statement( new_db, "INSERT INTO temporal_symbol_hash (sym_type,sym_const) VALUES (?,?)" );
584  add( hash_add );
585 }
586 
587 epmem_graph_statement_container::epmem_graph_statement_container( agent *new_agent ): soar_module::sqlite_statement_container( new_agent->epmem_db )
588 {
589  soar_module::sqlite_database *new_db = new_agent->epmem_db;
590 
591  //
592 
593  add_structure( "CREATE TABLE IF NOT EXISTS times (id INTEGER PRIMARY KEY)" );
594 
595  add_structure( "CREATE TABLE IF NOT EXISTS node_now (id INTEGER,start INTEGER)" );
596  add_structure( "CREATE INDEX IF NOT EXISTS node_now_start ON node_now (start)" );
597  add_structure( "CREATE UNIQUE INDEX IF NOT EXISTS node_now_id_start ON node_now (id,start DESC)" );
598 
599  add_structure( "CREATE TABLE IF NOT EXISTS edge_now (id INTEGER,start INTEGER)" );
600  add_structure( "CREATE INDEX IF NOT EXISTS edge_now_start ON edge_now (start)" );
601  add_structure( "CREATE UNIQUE INDEX IF NOT EXISTS edge_now_id_start ON edge_now (id,start DESC)" );
602 
603  add_structure( "CREATE TABLE IF NOT EXISTS node_point (id INTEGER,start INTEGER)" );
604  add_structure( "CREATE UNIQUE INDEX IF NOT EXISTS node_point_id_start ON node_point (id,start DESC)" );
605  add_structure( "CREATE INDEX IF NOT EXISTS node_point_start ON node_point (start)" );
606 
607  add_structure( "CREATE TABLE IF NOT EXISTS edge_point (id INTEGER,start INTEGER)" );
608  add_structure( "CREATE UNIQUE INDEX IF NOT EXISTS edge_point_id_start ON edge_point (id,start DESC)" );
609  add_structure( "CREATE INDEX IF NOT EXISTS edge_point_start ON edge_point (start)" );
610 
611  add_structure( "CREATE TABLE IF NOT EXISTS node_range (rit_node INTEGER,start INTEGER,end INTEGER,id INTEGER)" );
612  add_structure( "CREATE INDEX IF NOT EXISTS node_range_lower ON node_range (rit_node,start)" );
613  add_structure( "CREATE INDEX IF NOT EXISTS node_range_upper ON node_range (rit_node,end)" );
614  add_structure( "CREATE UNIQUE INDEX IF NOT EXISTS node_range_id_start ON node_range (id,start DESC)" );
615  add_structure( "CREATE UNIQUE INDEX IF NOT EXISTS node_range_id_end_start ON node_range (id,end DESC,start)" );
616 
617  add_structure( "CREATE TABLE IF NOT EXISTS edge_range (rit_node INTEGER,start INTEGER,end INTEGER,id INTEGER)" );
618  add_structure( "CREATE INDEX IF NOT EXISTS edge_range_lower ON edge_range (rit_node,start)" );
619  add_structure( "CREATE INDEX IF NOT EXISTS edge_range_upper ON edge_range (rit_node,end)" );
620  add_structure( "CREATE UNIQUE INDEX IF NOT EXISTS edge_range_id_start ON edge_range (id,start DESC)" );
621  add_structure( "CREATE UNIQUE INDEX IF NOT EXISTS edge_range_id_end_start ON edge_range (id,end DESC,start)" );
622 
623  add_structure( "CREATE TABLE IF NOT EXISTS node_unique (child_id INTEGER PRIMARY KEY AUTOINCREMENT,parent_id INTEGER,attrib INTEGER, value INTEGER)" );
624  add_structure( "CREATE UNIQUE INDEX IF NOT EXISTS node_unique_parent_attrib_value ON node_unique (parent_id,attrib,value)" );
625 
626  add_structure( "CREATE TABLE IF NOT EXISTS edge_unique (parent_id INTEGER PRIMARY KEY AUTOINCREMENT,q0 INTEGER,w INTEGER,q1 INTEGER, last INTEGER)" );
627  add_structure( "CREATE INDEX IF NOT EXISTS edge_unique_q0_w_last ON edge_unique (q0,w,last)" );
628  add_structure( "CREATE UNIQUE INDEX IF NOT EXISTS edge_unique_q0_w_q1 ON edge_unique (q0,w,q1)" );
629 
630  add_structure( "CREATE TABLE IF NOT EXISTS lti (parent_id INTEGER PRIMARY KEY, letter INTEGER, num INTEGER, time_id INTEGER)" );
631  add_structure( "CREATE UNIQUE INDEX IF NOT EXISTS lti_letter_num ON lti (letter,num)" );
632 
633  // adding an ascii table just to make lti queries easier when inspecting database
634  add_structure( "CREATE TABLE IF NOT EXISTS ascii (ascii_num INTEGER PRIMARY KEY, ascii_chr TEXT)" );
635  add_structure( "DELETE FROM ascii" );
636  {
637  add_structure( "INSERT INTO ascii (ascii_num, ascii_chr) VALUES (65,'A')" );
638  add_structure( "INSERT INTO ascii (ascii_num, ascii_chr) VALUES (66,'B')" );
639  add_structure( "INSERT INTO ascii (ascii_num, ascii_chr) VALUES (67,'C')" );
640  add_structure( "INSERT INTO ascii (ascii_num, ascii_chr) VALUES (68,'D')" );
641  add_structure( "INSERT INTO ascii (ascii_num, ascii_chr) VALUES (69,'E')" );
642  add_structure( "INSERT INTO ascii (ascii_num, ascii_chr) VALUES (70,'F')" );
643  add_structure( "INSERT INTO ascii (ascii_num, ascii_chr) VALUES (71,'G')" );
644  add_structure( "INSERT INTO ascii (ascii_num, ascii_chr) VALUES (72,'H')" );
645  add_structure( "INSERT INTO ascii (ascii_num, ascii_chr) VALUES (73,'I')" );
646  add_structure( "INSERT INTO ascii (ascii_num, ascii_chr) VALUES (74,'J')" );
647  add_structure( "INSERT INTO ascii (ascii_num, ascii_chr) VALUES (75,'K')" );
648  add_structure( "INSERT INTO ascii (ascii_num, ascii_chr) VALUES (76,'L')" );
649  add_structure( "INSERT INTO ascii (ascii_num, ascii_chr) VALUES (77,'M')" );
650  add_structure( "INSERT INTO ascii (ascii_num, ascii_chr) VALUES (78,'N')" );
651  add_structure( "INSERT INTO ascii (ascii_num, ascii_chr) VALUES (79,'O')" );
652  add_structure( "INSERT INTO ascii (ascii_num, ascii_chr) VALUES (80,'P')" );
653  add_structure( "INSERT INTO ascii (ascii_num, ascii_chr) VALUES (81,'Q')" );
654  add_structure( "INSERT INTO ascii (ascii_num, ascii_chr) VALUES (82,'R')" );
655  add_structure( "INSERT INTO ascii (ascii_num, ascii_chr) VALUES (83,'S')" );
656  add_structure( "INSERT INTO ascii (ascii_num, ascii_chr) VALUES (84,'T')" );
657  add_structure( "INSERT INTO ascii (ascii_num, ascii_chr) VALUES (85,'U')" );
658  add_structure( "INSERT INTO ascii (ascii_num, ascii_chr) VALUES (86,'V')" );
659  add_structure( "INSERT INTO ascii (ascii_num, ascii_chr) VALUES (87,'W')" );
660  add_structure( "INSERT INTO ascii (ascii_num, ascii_chr) VALUES (88,'X')" );
661  add_structure( "INSERT INTO ascii (ascii_num, ascii_chr) VALUES (89,'Y')" );
662  add_structure( "INSERT INTO ascii (ascii_num, ascii_chr) VALUES (90,'Z')" );
663  }
664 
665  //
666 
667  add_time = new soar_module::sqlite_statement( new_db, "INSERT INTO times (id) VALUES (?)" );
668  add( add_time );
669 
670  //
671 
672  add_node_now = new soar_module::sqlite_statement( new_db, "INSERT INTO node_now (id,start) VALUES (?,?)" );
673  add( add_node_now );
674 
675  delete_node_now = new soar_module::sqlite_statement( new_db, "DELETE FROM node_now WHERE id=?" );
676  add( delete_node_now );
677 
678  add_node_point = new soar_module::sqlite_statement( new_db, "INSERT INTO node_point (id,start) VALUES (?,?)" );
679  add( add_node_point );
680 
681  add_node_range = new soar_module::sqlite_statement( new_db, "INSERT INTO node_range (rit_node,start,end,id) VALUES (?,?,?,?)" );
682  add( add_node_range );
683 
684 
685  add_node_unique = new soar_module::sqlite_statement( new_db, "INSERT INTO node_unique (parent_id,attrib,value) VALUES (?,?,?)" );
686  add( add_node_unique );
687 
688  find_node_unique = new soar_module::sqlite_statement( new_db, "SELECT child_id FROM node_unique WHERE parent_id=? AND attrib=? AND value=?" );
690 
691  //
692 
693  add_edge_now = new soar_module::sqlite_statement( new_db, "INSERT INTO edge_now (id,start) VALUES (?,?)" );
694  add( add_edge_now );
695 
696  delete_edge_now = new soar_module::sqlite_statement( new_db, "DELETE FROM edge_now WHERE id=?" );
697  add( delete_edge_now );
698 
699  add_edge_point = new soar_module::sqlite_statement( new_db, "INSERT INTO edge_point (id,start) VALUES (?,?)" );
700  add( add_edge_point );
701 
702  add_edge_range = new soar_module::sqlite_statement( new_db, "INSERT INTO edge_range (rit_node,start,end,id) VALUES (?,?,?,?)" );
703  add( add_edge_range );
704 
705 
706  add_edge_unique = new soar_module::sqlite_statement( new_db, "INSERT INTO edge_unique (q0,w,q1,last) VALUES (?,?,?,?)" );
707  add( add_edge_unique );
708 
709  find_edge_unique = new soar_module::sqlite_statement( new_db, "SELECT parent_id, q1 FROM edge_unique WHERE q0=? AND w=?" );
711 
712  find_edge_unique_shared = new soar_module::sqlite_statement( new_db, "SELECT parent_id, q1 FROM edge_unique WHERE q0=? AND w=? AND q1=?" );
714 
715  //
716 
717  valid_episode = new soar_module::sqlite_statement( new_db, "SELECT COUNT(*) AS ct FROM times WHERE id=?" );
718  add( valid_episode );
719 
720  next_episode = new soar_module::sqlite_statement( new_db, "SELECT id FROM times WHERE id>? ORDER BY id ASC LIMIT 1" );
721  add( next_episode );
722 
723  prev_episode = new soar_module::sqlite_statement( new_db, "SELECT id FROM times WHERE id<? ORDER BY id DESC LIMIT 1" );
724  add( prev_episode );
725 
726 
727  get_nodes = new soar_module::sqlite_statement( new_db, "SELECT f.child_id, f.parent_id, h1.sym_const, h2.sym_const, h1.sym_type, h2.sym_type FROM node_unique f, temporal_symbol_hash h1, temporal_symbol_hash h2 WHERE f.child_id IN (SELECT n.id FROM node_now n WHERE n.start<= ? UNION ALL SELECT p.id FROM node_point p WHERE p.start=? UNION ALL SELECT e1.id FROM node_range e1, rit_left_nodes lt WHERE e1.rit_node=lt.min AND e1.end >= ? UNION ALL SELECT e2.id FROM node_range e2, rit_right_nodes rt WHERE e2.rit_node = rt.node AND e2.start <= ?) AND f.attrib=h1.id AND f.value=h2.id ORDER BY f.child_id ASC", new_agent->epmem_timers->ncb_node );
728  add( get_nodes );
729 
730  get_edges = new soar_module::sqlite_statement( new_db, "SELECT f.q0, h.sym_const, f.q1, h.sym_type, lti.letter, lti.num FROM edge_unique f INNER JOIN temporal_symbol_hash h ON f.w=h.id LEFT JOIN lti ON (f.q1=lti.parent_id AND lti.time_id <= ?) WHERE f.parent_id IN (SELECT n.id FROM edge_now n WHERE n.start<= ? UNION ALL SELECT p.id FROM edge_point p WHERE p.start = ? UNION ALL SELECT e1.id FROM edge_range e1, rit_left_nodes lt WHERE e1.rit_node=lt.min AND e1.end >= ? UNION ALL SELECT e2.id FROM edge_range e2, rit_right_nodes rt WHERE e2.rit_node = rt.node AND e2.start <= ?) ORDER BY f.q0 ASC, f.q1 ASC", new_agent->epmem_timers->ncb_edge );
731  add( get_edges );
732 
733  //
734 
735  promote_id = new soar_module::sqlite_statement( new_db, "INSERT OR IGNORE INTO lti (parent_id,letter,num,time_id) VALUES (?,?,?,?)" );
736  add( promote_id );
737 
738  find_lti = new soar_module::sqlite_statement( new_db, "SELECT parent_id FROM lti WHERE letter=? AND num=?" );
739  add( find_lti );
740 
741  find_lti_promotion_time = new soar_module::sqlite_statement( new_db, "SELECT time_id FROM lti WHERE parent_id=?" );
743 
744  //
745 
746  update_edge_unique_last = new soar_module::sqlite_statement( new_db, "UPDATE edge_unique SET last=? WHERE parent_id=?" );
748 
749  //
750 
751  // init statement pools
752  {
753  int j, k, m;
754 
755  const char* epmem_find_edge_queries[2][2] = {
756  {
757  "SELECT child_id, value, ? FROM node_unique WHERE parent_id=? AND attrib=?",
758  "SELECT child_id, value, ? FROM node_unique WHERE parent_id=? AND attrib=? AND value=?"
759  },
760  {
761  "SELECT parent_id, q1, last FROM edge_unique WHERE q0=? AND w=? AND ?<last ORDER BY last DESC",
762  "SELECT parent_id, q1, last FROM edge_unique WHERE q0=? AND w=? AND q1=? AND ?<last"
763  }
764  };
765 
766  for ( j=EPMEM_RIT_STATE_NODE; j<=EPMEM_RIT_STATE_EDGE; j++ )
767  {
768  for ( k=0; k<=1; k++ )
769  {
770  pool_find_edge_queries[ j ][ k ] = new soar_module::sqlite_statement_pool( new_agent, new_db, epmem_find_edge_queries[ j ][ k ] );
771  }
772  }
773 
774  //
775 
776  // Because the DB records when things are /inserted/, we need to offset
777  // the start by 1 to /remove/ them at the right time. Ditto to even
778  // include those intervals correctly
779  const char *epmem_find_interval_queries[2][2][3] =
780  {
781  {
782  {
783  "SELECT (e.start - 1) AS start FROM node_range e WHERE e.id=? AND e.start<=? ORDER BY e.start DESC",
784  "SELECT (e.start - 1) AS start FROM node_now e WHERE e.id=? AND e.start<=? ORDER BY e.start DESC",
785  "SELECT (e.start - 1) AS start FROM node_point e WHERE e.id=? AND e.start<=? ORDER BY e.start DESC"
786  },
787  {
788  "SELECT e.end AS end FROM node_range e WHERE e.id=? AND e.end>0 AND e.start<=? ORDER BY e.end DESC",
789  "SELECT ? AS end FROM node_now e WHERE e.id=? AND e.start<=? ORDER BY e.start DESC",
790  "SELECT e.start AS end FROM node_point e WHERE e.id=? AND e.start<=? ORDER BY e.start DESC"
791  }
792  },
793  {
794  {
795  "SELECT (e.start - 1) AS start FROM edge_range e WHERE e.id=? AND e.start<=? ORDER BY e.start DESC",
796  "SELECT (e.start - 1) AS start FROM edge_now e WHERE e.id=? AND e.start<=? ORDER BY e.start DESC",
797  "SELECT (e.start - 1) AS start FROM edge_point e WHERE e.id=? AND e.start<=? ORDER BY e.start DESC"
798  },
799  {
800  "SELECT e.end AS end FROM edge_range e WHERE e.id=? AND e.end>0 AND e.start<=? ORDER BY e.end DESC",
801  "SELECT ? AS end FROM edge_now e WHERE e.id=? AND e.start<=? ORDER BY e.start DESC",
802  "SELECT e.start AS end FROM edge_point e WHERE e.id=? AND e.start<=? ORDER BY e.start DESC"
803  }
804  },
805  };
806 
807  for ( j=EPMEM_RIT_STATE_NODE; j<=EPMEM_RIT_STATE_EDGE; j++ )
808  {
809  for ( k=EPMEM_RANGE_START; k<=EPMEM_RANGE_END; k++ )
810  {
811  for( m=EPMEM_RANGE_EP; m<=EPMEM_RANGE_POINT; m++ )
812  {
813  pool_find_interval_queries[ j ][ k ][ m ] = new soar_module::sqlite_statement_pool( new_agent, new_db, epmem_find_interval_queries[ j ][ k ][ m ] );
814  }
815  }
816  }
817 
818  //
819 
820  // notice that the start and end queries in epmem_find_lti_queries are _asymetric_
821  // in that the the starts have ?<e.start and the ends have ?<=e.start
822  // this small difference means that the start of the very first interval
823  // (ie. the one where the start is at or before the promotion time) will be ignored
824  // then we can simply add a single epmem_interval to the queue, and it will
825  // terminate any LTI interval appropriately
826  const char *epmem_find_lti_queries[2][3] =
827  {
828  {
829  "SELECT (e.start - 1) AS start FROM edge_range e WHERE e.id=? AND ?<e.start AND e.start<=? ORDER BY e.start DESC",
830  "SELECT (e.start - 1) AS start FROM edge_now e WHERE e.id=? AND ?<e.start AND e.start<=? ORDER BY e.start DESC",
831  "SELECT (e.start - 1) AS start FROM edge_point e WHERE e.id=? AND ?<e.start AND e.start<=? ORDER BY e.start DESC"
832  },
833  {
834  "SELECT e.end AS end FROM edge_range e WHERE e.id=? AND e.end>0 AND ?<=e.start AND e.start<=? ORDER BY e.end DESC",
835  "SELECT ? AS end FROM edge_now e WHERE e.id=? AND ?<=e.start AND e.start<=? ORDER BY e.start",
836  "SELECT e.start AS end FROM edge_point e WHERE e.id=? AND ?<=e.start AND e.start<=? ORDER BY e.start DESC"
837  }
838  };
839 
840  for ( k=EPMEM_RANGE_START; k<=EPMEM_RANGE_END; k++ )
841  {
842  for( m=EPMEM_RANGE_EP; m<=EPMEM_RANGE_POINT; m++ )
843  {
844  pool_find_lti_queries[ k ][ m ] = new soar_module::sqlite_statement_pool( new_agent, new_db, epmem_find_lti_queries[ k ][ m ] );
845  }
846  }
847 
848  //
849 
850  pool_dummy = new soar_module::sqlite_statement_pool( new_agent, new_db, "SELECT ? as start" );
851  }
852 }
853 
854 
857 // WME Functions (epmem::wmes)
860 
861 /***************************************************************************
862  * Function : epmem_get_augs_of_id
863  * Author : Nate Derbinsky
864  * Notes : This routine gets all wmes rooted at an id.
865  **************************************************************************/
867 {
868  slot *s;
869  wme *w;
870  epmem_wme_list *return_val = new epmem_wme_list;
871 
872  // augs only exist for identifiers
873  if ( ( id->common.symbol_type == IDENTIFIER_SYMBOL_TYPE ) &&
874  ( id->id.tc_num != tc ) )
875  {
876  id->id.tc_num = tc;
877 
878  // impasse wmes
879  for ( w=id->id.impasse_wmes; w!=NIL; w=w->next )
880  {
881  return_val->push_back( w );
882  }
883 
884  // input wmes
885  for ( w=id->id.input_wmes; w!=NIL; w=w->next )
886  {
887  return_val->push_back( w );
888  }
889 
890  // regular wmes
891  for ( s=id->id.slots; s!=NIL; s=s->next )
892  {
893  for ( w=s->wmes; w!=NIL; w=w->next )
894  {
895  return_val->push_back( w );
896  }
897 
898  for ( w=s->acceptable_preference_wmes; w!=NIL; w=w->next )
899  {
900  return_val->push_back( w );
901  }
902  }
903  }
904 
905  return return_val;
906 }
907 
909 {
910  if ( my_list.empty() )
911  {
912  return;
913  }
914 
915  instantiation* inst = soar_module::make_fake_instantiation( my_agent, state, &cue_wmes, &my_list );
916 
917  for ( preference* pref=inst->preferences_generated; pref; pref=pref->inst_next )
918  {
919  // add the preference to temporary memory
920  if ( add_preference_to_tm( my_agent, pref ) )
921  {
922  // add to the list of preferences to be removed
923  // when the goal is removed
924  insert_at_head_of_dll( state->id.preferences_from_goal, pref, all_of_goal_next, all_of_goal_prev );
925  pref->on_goal_list = true;
926 
927  if ( epmem_wmes )
928  {
929  // if this is a meta wme, then it is completely local
930  // to the state and thus we will manually remove it
931  // (via preference removal) when the time comes
932  epmem_wmes->push_back( pref );
933  }
934  }
935  else
936  {
937  preference_add_ref( pref );
938  preference_remove_ref( my_agent, pref );
939  }
940  }
941 
942  if ( !epmem_wmes )
943  {
944  // otherwise, we submit the fake instantiation to backtracing
945  // such as to potentially produce justifications that can follow
946  // it to future adventures (potentially on new states)
947  instantiation *my_justification_list = NIL;
948  chunk_instantiation( my_agent, inst, false, &my_justification_list );
949 
950  // if any justifications are created, assert their preferences manually
951  // (copied mainly from assert_new_preferences with respect to our circumstances)
952  if ( my_justification_list != NIL )
953  {
954  preference *just_pref = NIL;
955  instantiation *next_justification = NIL;
956 
957  for ( instantiation *my_justification=my_justification_list;
958  my_justification!=NIL;
959  my_justification=next_justification )
960  {
961  next_justification = my_justification->next;
962 
963  if ( my_justification->in_ms )
964  {
965  insert_at_head_of_dll( my_justification->prod->instantiations, my_justification, next, prev );
966  }
967 
968  for ( just_pref=my_justification->preferences_generated; just_pref!=NIL; just_pref=just_pref->inst_next )
969  {
970  if ( add_preference_to_tm( my_agent, just_pref ) )
971  {
972  if ( wma_enabled( my_agent ) )
973  {
974  wma_activate_wmes_in_pref( my_agent, just_pref );
975  }
976  }
977  else
978  {
979  preference_add_ref( just_pref );
980  preference_remove_ref( my_agent, just_pref );
981  }
982  }
983  }
984  }
985  }
986 }
987 
989 {
990  _epmem_process_buffered_wme_list( my_agent, state, cue_wmes, meta_wmes, state->id.epmem_info->epmem_wmes );
991  _epmem_process_buffered_wme_list( my_agent, state, cue_wmes, retrieval_wmes, NULL );
992 }
993 
994 inline void epmem_buffer_add_wme( soar_module::symbol_triple_list& my_list, Symbol* id, Symbol* attr, Symbol* value )
995 {
996  my_list.push_back( new soar_module::symbol_triple( id, attr, value ) );
997 
998  symbol_add_ref( id );
999  symbol_add_ref( attr );
1000  symbol_add_ref( value );
1001 }
1002 
1003 
1006 // Variable Functions (epmem::var)
1007 //
1008 // Variables are key-value pairs stored in the database
1009 // that are necessary to maintain a store between
1010 // multiple runs of Soar.
1011 //
1014 
1015 /***************************************************************************
1016  * Function : epmem_get_variable
1017  * Author : Nate Derbinsky
1018  * Notes : Gets an EpMem variable from the database
1019  **************************************************************************/
1020 bool epmem_get_variable( agent *my_agent, epmem_variable_key variable_id, int64_t *variable_value )
1021 {
1022  soar_module::exec_result status;
1024 
1025  var_get->bind_int( 1, variable_id );
1026  status = var_get->execute();
1027 
1028  if ( status == soar_module::row )
1029  {
1030  (*variable_value) = var_get->column_int( 0 );
1031  }
1032 
1033  var_get->reinitialize();
1034 
1035  return ( status == soar_module::row );
1036 }
1037 
1038 /***************************************************************************
1039  * Function : epmem_set_variable
1040  * Author : Nate Derbinsky
1041  * Notes : Sets an EpMem variable in the database
1042  **************************************************************************/
1043 void epmem_set_variable( agent *my_agent, epmem_variable_key variable_id, int64_t variable_value )
1044 {
1046 
1047  var_set->bind_int( 1, variable_id );
1048  var_set->bind_int( 2, variable_value );
1049 
1050  var_set->execute( soar_module::op_reinit );
1051 }
1052 
1055 // RIT Functions (epmem::rit)
1058 
1059 /***************************************************************************
1060  * Function : epmem_rit_fork_node
1061  * Author : Nate Derbinsky
1062  * Notes : Implements the forkNode function of RIT
1063  **************************************************************************/
1064 int64_t epmem_rit_fork_node( int64_t lower, int64_t upper, bool /*bounds_offset*/, int64_t *step_return, epmem_rit_state *rit_state )
1065 {
1066  // never called
1067  /*if ( !bounds_offset )
1068  {
1069  int64_t offset = rit_state->offset.stat->get_value();
1070 
1071  lower = ( lower - offset );
1072  upper = ( upper - offset );
1073  }*/
1074 
1075  // descend the tree down to the fork node
1076  int64_t node = EPMEM_RIT_ROOT;
1077  if ( upper < EPMEM_RIT_ROOT )
1078  {
1079  node = rit_state->leftroot.stat->get_value();
1080  }
1081  else if ( lower > EPMEM_RIT_ROOT )
1082  {
1083  node = rit_state->rightroot.stat->get_value();
1084  }
1085 
1086  int64_t step;
1087  for ( step = ( ( ( node >= 0 )?( node ):( -1 * node ) ) / 2 ); step >= 1; step /= 2 )
1088  {
1089  if ( upper < node )
1090  {
1091  node -= step;
1092  }
1093  else if ( node < lower )
1094  {
1095  node += step;
1096  }
1097  else
1098  {
1099  break;
1100  }
1101  }
1102 
1103  // never used
1104  // if ( step_return != NULL )
1105  {
1106  (*step_return) = step;
1107  }
1108 
1109  return node;
1110 }
1111 
1112 /***************************************************************************
1113  * Function : epmem_rit_clear_left_right
1114  * Author : Nate Derbinsky
1115  * Notes : Clears the left/right relations populated during prep
1116  **************************************************************************/
1118 {
1121 }
1122 
1123 /***************************************************************************
1124  * Function : epmem_rit_add_left
1125  * Author : Nate Derbinsky
1126  * Notes : Adds a range to the left relation
1127  **************************************************************************/
1129 {
1130  my_agent->epmem_stmts_common->rit_add_left->bind_int( 1, min );
1131  my_agent->epmem_stmts_common->rit_add_left->bind_int( 2, max );
1133 }
1134 
1135 /***************************************************************************
1136  * Function : epmem_rit_add_right
1137  * Author : Nate Derbinsky
1138  * Notes : Adds a node to the to the right relation
1139  **************************************************************************/
1141 {
1142  my_agent->epmem_stmts_common->rit_add_right->bind_int( 1, id );
1144 }
1145 
1146 /***************************************************************************
1147  * Function : epmem_rit_prep_left_right
1148  * Author : Nate Derbinsky
1149  * Notes : Implements the computational components of the RIT
1150  * query algorithm
1151  **************************************************************************/
1152 void epmem_rit_prep_left_right( agent *my_agent, int64_t lower, int64_t upper, epmem_rit_state *rit_state )
1153 {
1155  rit_state->timer->start();
1157 
1158  int64_t offset = rit_state->offset.stat->get_value();
1159  int64_t node, step;
1160  int64_t left_node, left_step;
1161  int64_t right_node, right_step;
1162 
1163  lower = ( lower - offset );
1164  upper = ( upper - offset );
1165 
1166  // auto add good range
1167  epmem_rit_add_left( my_agent, lower, upper );
1168 
1169  // go to fork
1170  node = EPMEM_RIT_ROOT;
1171  step = 0;
1172  if ( ( lower > node ) || (upper < node ) )
1173  {
1174  if ( lower > node )
1175  {
1176  node = rit_state->rightroot.stat->get_value();
1178  }
1179  else
1180  {
1181  node = rit_state->leftroot.stat->get_value();
1182  epmem_rit_add_right( my_agent, EPMEM_RIT_ROOT );
1183  }
1184 
1185  for ( step = ( ( ( node >= 0 )?( node ):( -1 * node ) ) / 2 ); step >= 1; step /= 2 )
1186  {
1187  if ( lower > node )
1188  {
1189  epmem_rit_add_left( my_agent, node, node );
1190  node += step;
1191  }
1192  else if ( upper < node )
1193  {
1194  epmem_rit_add_right( my_agent, node );
1195  node -= step;
1196  }
1197  else
1198  {
1199  break;
1200  }
1201  }
1202  }
1203 
1204  // go left
1205  left_node = node - step;
1206  for ( left_step = ( step / 2 ); left_step >= 1; left_step /= 2 )
1207  {
1208  if ( lower == left_node )
1209  {
1210  break;
1211  }
1212  else if ( lower > left_node )
1213  {
1214  epmem_rit_add_left( my_agent, left_node, left_node );
1215  left_node += left_step;
1216  }
1217  else
1218  {
1219  left_node -= left_step;
1220  }
1221  }
1222 
1223  // go right
1224  right_node = node + step;
1225  for ( right_step = ( step / 2 ); right_step >= 1; right_step /= 2 )
1226  {
1227  if ( upper == right_node )
1228  {
1229  break;
1230  }
1231  else if ( upper < right_node )
1232  {
1233  epmem_rit_add_right( my_agent, right_node );
1234  right_node -= right_step;
1235  }
1236  else
1237  {
1238  right_node += right_step;
1239  }
1240  }
1241 
1243  rit_state->timer->stop();
1245 }
1246 
1247 /***************************************************************************
1248  * Function : epmem_rit_insert_interval
1249  * Author : Nate Derbinsky
1250  * Notes : Inserts an interval in the RIT
1251  **************************************************************************/
1252 void epmem_rit_insert_interval( agent *my_agent, int64_t lower, int64_t upper, epmem_node_id id, epmem_rit_state *rit_state )
1253 {
1254  // initialize offset
1255  int64_t offset = rit_state->offset.stat->get_value();
1256  if ( offset == EPMEM_RIT_OFFSET_INIT )
1257  {
1258  offset = lower;
1259 
1260  // update database
1261  epmem_set_variable( my_agent, rit_state->offset.var_key, offset );
1262 
1263  // update stat
1264  rit_state->offset.stat->set_value( offset );
1265  }
1266 
1267  // get node
1268  int64_t node;
1269  {
1270  int64_t left_root = rit_state->leftroot.stat->get_value();
1271  int64_t right_root = rit_state->rightroot.stat->get_value();
1272  int64_t min_step = rit_state->minstep.stat->get_value();
1273 
1274  // shift interval
1275  int64_t l = ( lower - offset );
1276  int64_t u = ( upper - offset );
1277 
1278  // update left_root
1279  if ( ( u < EPMEM_RIT_ROOT ) && ( l <= ( 2 * left_root ) ) )
1280  {
1281  left_root = static_cast<int64_t>( pow( -2.0, floor( log( static_cast<double>( -l ) ) / EPMEM_LN_2 ) ) );
1282 
1283  // update database
1284  epmem_set_variable( my_agent, rit_state->leftroot.var_key, left_root );
1285 
1286  // update stat
1287  rit_state->leftroot.stat->set_value( left_root );
1288  }
1289 
1290  // update right_root
1291  if ( ( l > EPMEM_RIT_ROOT ) && ( u >= ( 2 * right_root ) ) )
1292  {
1293  right_root = static_cast<int64_t>( pow( 2.0, floor( log( static_cast<double>( u ) ) / EPMEM_LN_2 ) ) );
1294 
1295  // update database
1296  epmem_set_variable( my_agent, rit_state->rightroot.var_key, right_root );
1297 
1298  // update stat
1299  rit_state->rightroot.stat->set_value( right_root );
1300  }
1301 
1302  // update min_step
1303  int64_t step;
1304  node = epmem_rit_fork_node( l, u, true, &step, rit_state );
1305 
1306  if ( ( node != EPMEM_RIT_ROOT ) && ( step < min_step ) )
1307  {
1308  min_step = step;
1309 
1310  // update database
1311  epmem_set_variable( my_agent, rit_state->minstep.var_key, min_step );
1312 
1313  // update stat
1314  rit_state->minstep.stat->set_value( min_step );
1315  }
1316  }
1317 
1318  // perform insert
1319  // ( node, start, end, id )
1320  rit_state->add_query->bind_int( 1, node );
1321  rit_state->add_query->bind_int( 2, lower );
1322  rit_state->add_query->bind_int( 3, upper );
1323  rit_state->add_query->bind_int( 4, id );
1324  rit_state->add_query->execute( soar_module::op_reinit );
1325 }
1326 
1327 
1330 // Clean-Up Functions (epmem::clean)
1333 
1334 /***************************************************************************
1335  * Function : epmem_close
1336  * Author : Nate Derbinsky
1337  * Notes : Performs cleanup operations when the database needs
1338  * to be closed (end soar, manual close, etc)
1339  **************************************************************************/
1340 void epmem_close( agent *my_agent )
1341 {
1342  if ( my_agent->epmem_db->get_status() == soar_module::connected )
1343  {
1344  // if lazy, commit
1345  if ( my_agent->epmem_params->lazy_commit->get_value() == soar_module::on )
1346  {
1348  }
1349 
1350  // de-allocate statement pools
1351  {
1352  int j, k, m;
1353 
1354  for ( j=EPMEM_RIT_STATE_NODE; j<=EPMEM_RIT_STATE_EDGE; j++ )
1355  {
1356  for ( k=0; k<=1; k++ )
1357  {
1358  delete my_agent->epmem_stmts_graph->pool_find_edge_queries[ j ][ k ];
1359  }
1360  }
1361 
1362  for ( j=EPMEM_RIT_STATE_NODE; j<=EPMEM_RIT_STATE_EDGE; j++ )
1363  {
1364  for ( k=EPMEM_RANGE_START; k<=EPMEM_RANGE_END; k++ )
1365  {
1366  for( m=EPMEM_RANGE_EP; m<=EPMEM_RANGE_POINT; m++ )
1367  {
1368  delete my_agent->epmem_stmts_graph->pool_find_interval_queries[ j ][ k ][ m ];
1369  }
1370  }
1371  }
1372 
1373  for ( k=EPMEM_RANGE_START; k<=EPMEM_RANGE_END; k++ )
1374  {
1375  for( m=EPMEM_RANGE_EP; m<=EPMEM_RANGE_POINT; m++ )
1376  {
1377  delete my_agent->epmem_stmts_graph->pool_find_lti_queries[ k ][ m ];
1378  }
1379  }
1380 
1381  delete my_agent->epmem_stmts_graph->pool_dummy;
1382  }
1383 
1384  // de-allocate statements
1385  delete my_agent->epmem_stmts_common;
1386  delete my_agent->epmem_stmts_graph;
1387 
1388  // de-allocate local data structures
1389  {
1390  epmem_parent_id_pool::iterator p;
1391  epmem_hashed_id_pool::iterator p_p;
1392 
1393  for ( p=my_agent->epmem_id_repository->begin(); p!=my_agent->epmem_id_repository->end(); p++ )
1394  {
1395  for ( p_p=p->second->begin(); p_p!=p->second->end(); p_p++ )
1396  {
1397  delete p_p->second;
1398  }
1399 
1400  delete p->second;
1401  }
1402 
1403  my_agent->epmem_id_repository->clear();
1404  my_agent->epmem_id_replacement->clear();
1405  for ( epmem_id_ref_counter::iterator rf_it=my_agent->epmem_id_ref_counts->begin(); rf_it!=my_agent->epmem_id_ref_counts->end(); rf_it++ )
1406  {
1407  delete rf_it->second;
1408  }
1409  my_agent->epmem_id_ref_counts->clear();
1410 
1411  my_agent->epmem_wme_adds->clear();
1412 
1413  for ( epmem_symbol_set::iterator p_it=my_agent->epmem_promotions->begin(); p_it!=my_agent->epmem_promotions->end(); p_it++ )
1414  {
1415  symbol_remove_ref( my_agent, (*p_it) );
1416  }
1417  my_agent->epmem_promotions->clear();
1418  }
1419 
1420  // close the database
1421  my_agent->epmem_db->disconnect();
1422  }
1423 
1424 #ifdef EPMEM_EXPERIMENT
1425  if ( epmem_exp_output )
1426  {
1427  epmem_exp_output->close();
1428  delete epmem_exp_output;
1429  epmem_exp_output = NULL;
1430 
1431  if ( epmem_exp_timer )
1432  {
1433  delete epmem_exp_timer;
1434  }
1435  }
1436 #endif
1437 }
1438 
1439 /***************************************************************************
1440  * Function : epmem_clear_result
1441  * Author : Nate Derbinsky
1442  * Notes : Removes any WMEs produced by EpMem resulting from
1443  * a command
1444  **************************************************************************/
1445 void epmem_clear_result( agent *my_agent, Symbol *state )
1446 {
1447  preference *pref;
1448 
1449  while ( !state->id.epmem_info->epmem_wmes->empty() )
1450  {
1451  pref = state->id.epmem_info->epmem_wmes->back();
1452  state->id.epmem_info->epmem_wmes->pop_back();
1453 
1454  if ( pref->in_tm )
1455  {
1456  remove_preference_from_tm( my_agent, pref );
1457  }
1458  }
1459 }
1460 
1461 /***************************************************************************
1462  * Function : epmem_reset
1463  * Author : Nate Derbinsky
1464  * Notes : Performs cleanup when a state is removed
1465  **************************************************************************/
1466 void epmem_reset( agent *my_agent, Symbol *state )
1467 {
1468  if ( state == NULL )
1469  {
1470  state = my_agent->top_goal;
1471  }
1472 
1473  while( state )
1474  {
1475  epmem_data *data = state->id.epmem_info;
1476 
1477  data->last_ol_time = 0;
1478 
1479  data->last_cmd_time = 0;
1480  data->last_cmd_count = 0;
1481 
1482  data->last_memory = EPMEM_MEMID_NONE;
1483 
1484  // this will be called after prefs from goal are already removed,
1485  // so just clear out result stack
1486  data->epmem_wmes->clear();
1487 
1488  state = state->id.lower_goal;
1489  }
1490 }
1491 
1492 
1495 // Initialization Functions (epmem::init)
1498 
1499 /***************************************************************************
1500  * Function : epmem_init_db
1501  * Author : Nate Derbinsky
1502  * Notes : Opens the SQLite database and performs all
1503  * initialization required for the current mode
1504  *
1505  * The readonly param should only be used in
1506  * experimentation where you don't want to alter
1507  * previous database state.
1508  **************************************************************************/
1509 void epmem_init_db( agent *my_agent, bool readonly = false )
1510 {
1511  if ( my_agent->epmem_db->get_status() != soar_module::disconnected )
1512  {
1513  return;
1514  }
1515 
1517  my_agent->epmem_timers->init->start();
1519 
1520  const char *db_path;
1522  {
1523  db_path = ":memory:";
1524  }
1525  else
1526  {
1527  db_path = my_agent->epmem_params->path->get_value();
1528  }
1529 
1530  // attempt connection
1531  my_agent->epmem_db->connect( db_path );
1532 
1533  if ( my_agent->epmem_db->get_status() == soar_module::problem )
1534  {
1535  char buf[256];
1536  SNPRINTF( buf, 254, "DB ERROR: %s", my_agent->epmem_db->get_errmsg() );
1537 
1538  print( my_agent, buf );
1539  xml_generate_warning( my_agent, buf );
1540  }
1541  else
1542  {
1543  epmem_time_id time_max;
1544  soar_module::sqlite_statement *temp_q = NULL;
1545  soar_module::sqlite_statement *temp_q2 = NULL;
1546 
1547  // apply performance options
1548  {
1549  // page_size
1550  {
1551  switch ( my_agent->epmem_params->page_size->get_value() )
1552  {
1554  temp_q = new soar_module::sqlite_statement( my_agent->epmem_db, "PRAGMA page_size = 1024" );
1555  break;
1556 
1558  temp_q = new soar_module::sqlite_statement( my_agent->epmem_db, "PRAGMA page_size = 2048" );
1559  break;
1560 
1562  temp_q = new soar_module::sqlite_statement( my_agent->epmem_db, "PRAGMA page_size = 4096" );
1563  break;
1564 
1566  temp_q = new soar_module::sqlite_statement( my_agent->epmem_db, "PRAGMA page_size = 8192" );
1567  break;
1568 
1570  temp_q = new soar_module::sqlite_statement( my_agent->epmem_db, "PRAGMA page_size = 16384" );
1571  break;
1572 
1574  temp_q = new soar_module::sqlite_statement( my_agent->epmem_db, "PRAGMA page_size = 32768" );
1575  break;
1576 
1578  temp_q = new soar_module::sqlite_statement( my_agent->epmem_db, "PRAGMA page_size = 65536" );
1579  break;
1580  }
1581 
1582  temp_q->prepare();
1583  temp_q->execute();
1584  delete temp_q;
1585  temp_q = NULL;
1586  }
1587 
1588  // cache_size
1589  {
1590  std::string cache_sql( "PRAGMA cache_size = " );
1591  char* str = my_agent->epmem_params->cache_size->get_string();
1592  cache_sql.append( str );
1593  free(str);
1594  str = NULL;
1595 
1596  temp_q = new soar_module::sqlite_statement( my_agent->epmem_db, cache_sql.c_str() );
1597 
1598  temp_q->prepare();
1599  temp_q->execute();
1600  delete temp_q;
1601  temp_q = NULL;
1602  }
1603 
1604  // optimization
1606  {
1607  // synchronous - don't wait for writes to complete (can corrupt the db in case unexpected crash during transaction)
1608  temp_q = new soar_module::sqlite_statement( my_agent->epmem_db, "PRAGMA synchronous = OFF" );
1609  temp_q->prepare();
1610  temp_q->execute();
1611  delete temp_q;
1612  temp_q = NULL;
1613 
1614  // journal_mode - no atomic transactions (can result in database corruption if crash during transaction)
1615  temp_q = new soar_module::sqlite_statement( my_agent->epmem_db, "PRAGMA journal_mode = OFF" );
1616  temp_q->prepare();
1617  temp_q->execute();
1618  delete temp_q;
1619  temp_q = NULL;
1620 
1621  // locking_mode - no one else can view the database after our first write
1622  temp_q = new soar_module::sqlite_statement( my_agent->epmem_db, "PRAGMA locking_mode = EXCLUSIVE" );
1623  temp_q->prepare();
1624  temp_q->execute();
1625  delete temp_q;
1626  temp_q = NULL;
1627  }
1628  }
1629 
1630  // point stuff
1631  epmem_time_id range_start;
1632  epmem_time_id time_last;
1633 
1634  // update validation count
1635  my_agent->epmem_validation++;
1636 
1637  // setup common structures/queries
1638  my_agent->epmem_stmts_common = new epmem_common_statement_container( my_agent );
1639  my_agent->epmem_stmts_common->structure();
1640  my_agent->epmem_stmts_common->prepare();
1641 
1642  {
1643  // setup graph structures/queries
1644  my_agent->epmem_stmts_graph = new epmem_graph_statement_container( my_agent );
1645  my_agent->epmem_stmts_graph->structure();
1646  my_agent->epmem_stmts_graph->prepare();
1647 
1648  // initialize range tracking
1649  my_agent->epmem_node_mins->clear();
1650  my_agent->epmem_node_maxes->clear();
1651  my_agent->epmem_node_removals->clear();
1652 
1653  my_agent->epmem_edge_mins->clear();
1654  my_agent->epmem_edge_maxes->clear();
1655  my_agent->epmem_edge_removals->clear();
1656 
1658  {
1659 #ifdef USE_MEM_POOL_ALLOCATORS
1660  epmem_wme_set* wms_temp = new epmem_wme_set( std::less< wme* >(), soar_module::soar_memory_pool_allocator< wme* >( my_agent ) );
1661 #else
1662  epmem_wme_set* wms_temp = new epmem_wme_set();
1663 #endif
1664 
1665  // voigtjr: Cast to wme* is necessary for compilation in VS10
1666  // Without it, it picks insert(int) instead of insert(wme*) and does not compile.
1667  wms_temp->insert( static_cast<wme*>(NULL) );
1668 
1669  (*my_agent->epmem_id_ref_counts)[ EPMEM_NODEID_ROOT ] = wms_temp;
1670  }
1671 
1672  // initialize time
1673  my_agent->epmem_stats->time->set_value( 1 );
1674 
1675  // initialize next_id
1676  my_agent->epmem_stats->next_id->set_value( 1 );
1677  {
1678  int64_t stored_id = NIL;
1679  if ( epmem_get_variable( my_agent, var_next_id, &stored_id ) )
1680  {
1681  my_agent->epmem_stats->next_id->set_value( stored_id );
1682  }
1683  else
1684  {
1685  epmem_set_variable( my_agent, var_next_id, my_agent->epmem_stats->next_id->get_value() );
1686  }
1687  }
1688 
1689  // initialize rit state
1690  for ( int i=EPMEM_RIT_STATE_NODE; i<=EPMEM_RIT_STATE_EDGE; i++ )
1691  {
1693  my_agent->epmem_rit_state_graph[ i ].leftroot.stat->set_value( 0 );
1694  my_agent->epmem_rit_state_graph[ i ].rightroot.stat->set_value( 1 );
1695  my_agent->epmem_rit_state_graph[ i ].minstep.stat->set_value( LONG_MAX );
1696  }
1699 
1701 
1702  // get/set RIT variables
1703  {
1704  int64_t var_val = NIL;
1705 
1706  for ( int i=EPMEM_RIT_STATE_NODE; i<=EPMEM_RIT_STATE_EDGE; i++ )
1707  {
1708  // offset
1709  if ( epmem_get_variable( my_agent, my_agent->epmem_rit_state_graph[ i ].offset.var_key, &var_val ) )
1710  {
1711  my_agent->epmem_rit_state_graph[ i ].offset.stat->set_value( var_val );
1712  }
1713  else
1714  {
1715  epmem_set_variable( my_agent, my_agent->epmem_rit_state_graph[ i ].offset.var_key, my_agent->epmem_rit_state_graph[ i ].offset.stat->get_value() );
1716  }
1717 
1718  // leftroot
1719  if ( epmem_get_variable( my_agent, my_agent->epmem_rit_state_graph[ i ].leftroot.var_key, &var_val ) )
1720  {
1721  my_agent->epmem_rit_state_graph[ i ].leftroot.stat->set_value( var_val );
1722  }
1723  else
1724  {
1726  }
1727 
1728  // rightroot
1729  if ( epmem_get_variable( my_agent, my_agent->epmem_rit_state_graph[ i ].rightroot.var_key, &var_val ) )
1730  {
1731  my_agent->epmem_rit_state_graph[ i ].rightroot.stat->set_value( var_val );
1732  }
1733  else
1734  {
1736  }
1737 
1738  // minstep
1739  if ( epmem_get_variable( my_agent, my_agent->epmem_rit_state_graph[ i ].minstep.var_key, &var_val ) )
1740  {
1741  my_agent->epmem_rit_state_graph[ i ].minstep.stat->set_value( var_val );
1742  }
1743  else
1744  {
1745  epmem_set_variable( my_agent, my_agent->epmem_rit_state_graph[ i ].minstep.var_key, my_agent->epmem_rit_state_graph[ i ].minstep.stat->get_value() );
1746  }
1747  }
1748  }
1749 
1751 
1752  // get max time
1753  {
1754  temp_q = new soar_module::sqlite_statement( my_agent->epmem_db, "SELECT MAX(id) FROM times" );
1755  temp_q->prepare();
1756  if ( temp_q->execute() == soar_module::row )
1757  my_agent->epmem_stats->time->set_value( temp_q->column_int( 0 ) + 1 );
1758 
1759  delete temp_q;
1760  temp_q = NULL;
1761  }
1762  time_max = my_agent->epmem_stats->time->get_value();
1763 
1764  // insert non-NOW intervals for all current NOW's
1765  // remove NOW's
1766  if ( !readonly )
1767  {
1768  time_last = ( time_max - 1 );
1769 
1770  const char *now_select[] = { "SELECT id,start FROM node_now", "SELECT id,start FROM edge_now" };
1772  const char *now_delete[] = { "DELETE FROM node_now", "DELETE FROM edge_now" };
1773 
1774  for ( int i=EPMEM_RIT_STATE_NODE; i<=EPMEM_RIT_STATE_EDGE; i++ )
1775  {
1776  temp_q = now_add[i];
1777  temp_q->bind_int( 2, time_last );
1778 
1779  temp_q2 = new soar_module::sqlite_statement( my_agent->epmem_db, now_select[i] );
1780  temp_q2->prepare();
1781  while ( temp_q2->execute() == soar_module::row )
1782  {
1783  range_start = temp_q2->column_int( 1 );
1784 
1785  // point
1786  if ( range_start == time_last )
1787  {
1788  temp_q->bind_int( 1, temp_q2->column_int( 0 ) );
1789  temp_q->execute( soar_module::op_reinit );
1790  }
1791  else
1792  {
1793  epmem_rit_insert_interval( my_agent, range_start, time_last, temp_q2->column_int( 0 ), &( my_agent->epmem_rit_state_graph[i] ) );
1794  }
1795 
1796  if ( i == EPMEM_RIT_STATE_EDGE)
1797  {
1798  my_agent->epmem_stmts_graph->update_edge_unique_last->bind_int( 1, time_last );
1799  my_agent->epmem_stmts_graph->update_edge_unique_last->bind_int( 2, temp_q2->column_int( 0 ) );
1801  }
1802  }
1803  delete temp_q2;
1804  temp_q2 = NULL;
1805  temp_q = NULL;
1806 
1807 
1808  // remove all NOW intervals
1809  temp_q = new soar_module::sqlite_statement( my_agent->epmem_db, now_delete[i] );
1810  temp_q->prepare();
1811  temp_q->execute();
1812  delete temp_q;
1813  temp_q = NULL;
1814  }
1815  }
1816 
1817  // get max id + max list
1818  {
1819  const char *minmax_select[] = { "SELECT MAX(child_id) FROM node_unique", "SELECT MAX(parent_id) FROM edge_unique" };
1820  std::vector<bool> *minmax_max[] = { my_agent->epmem_node_maxes, my_agent->epmem_edge_maxes };
1821  std::vector<epmem_time_id> *minmax_min[] = { my_agent->epmem_node_mins, my_agent->epmem_edge_mins };
1822 
1823  for ( int i=EPMEM_RIT_STATE_NODE; i<=EPMEM_RIT_STATE_EDGE; i++ )
1824  {
1825  temp_q = new soar_module::sqlite_statement( my_agent->epmem_db, minmax_select[i] );
1826  temp_q->prepare();
1827  temp_q->execute();
1828  if ( temp_q->column_type( 0 ) != soar_module::null_t )
1829  {
1830  std::vector<bool>::size_type num_ids = temp_q->column_int( 0 );
1831 
1832  minmax_max[i]->resize( num_ids, true );
1833  minmax_min[i]->resize( num_ids, time_max );
1834  }
1835 
1836  delete temp_q;
1837  temp_q = NULL;
1838  }
1839  }
1840 
1841  // get id pools
1842  {
1843  epmem_node_id q0;
1844  int64_t w;
1845  epmem_node_id q1;
1846  epmem_node_id parent_id;
1847 
1848  epmem_hashed_id_pool **hp;
1849  epmem_id_pool **ip;
1850 
1851  temp_q = new soar_module::sqlite_statement( my_agent->epmem_db, "SELECT q0, w, q1, parent_id FROM edge_unique" );
1852  temp_q->prepare();
1853 
1854  while ( temp_q->execute() == soar_module::row )
1855  {
1856  q0 = temp_q->column_int( 0 );
1857  w = temp_q->column_int( 1 );
1858  q1 = temp_q->column_int( 2 );
1859  parent_id = temp_q->column_int( 3 );
1860 
1861  hp =& (*my_agent->epmem_id_repository)[ q0 ];
1862  if ( !(*hp) )
1863  (*hp) = new epmem_hashed_id_pool;
1864 
1865  ip =& (*(*hp))[ w ];
1866  if ( !(*ip) )
1867  (*ip) = new epmem_id_pool;
1868 
1869  (*ip)->push_front( std::make_pair( q1, parent_id ) );
1870 
1871  hp =& (*my_agent->epmem_id_repository)[ q1 ];
1872  if ( !(*hp) )
1873  (*hp) = new epmem_hashed_id_pool;
1874  }
1875 
1876  delete temp_q;
1877  temp_q = NULL;
1878  }
1879 
1880  // at init, top-state is considered the only known identifier
1881  my_agent->top_goal->id.epmem_id = EPMEM_NODEID_ROOT;
1882  my_agent->top_goal->id.epmem_valid = my_agent->epmem_validation;
1883 
1884  // capture augmentations of top-state as the sole set of adds,
1885  // which catches up to what would have been incremental encoding
1886  // to this point
1887  {
1888  my_agent->epmem_wme_adds->insert( my_agent->top_state );
1889  }
1890  }
1891 
1892  // if lazy commit, then we encapsulate the entire lifetime of the agent in a single transaction
1893  if ( my_agent->epmem_params->lazy_commit->get_value() == soar_module::on )
1894  {
1896  }
1897  }
1898 
1900  my_agent->epmem_timers->init->stop();
1902 }
1903 
1904 
1907 // Temporal Hash Functions (epmem::hash)
1908 //
1909 // The rete has symbol hashing, but the values are
1910 // reliable only for the lifetime of a symbol. This
1911 // isn't good for EpMem. Hence, we implement a simple
1912 // lookup table, relying upon SQLite to deal with
1913 // efficiency issues.
1914 //
1917 
1918 /***************************************************************************
1919  * Function : epmem_temporal_hash
1920  * Author : Nate Derbinsky
1921  * Notes : Returns a temporally unique integer representing
1922  * a symbol constant.
1923  **************************************************************************/
1924 epmem_hash_id epmem_temporal_hash( agent *my_agent, Symbol *sym, bool add_on_fail = true )
1925 {
1926  epmem_hash_id return_val = NIL;
1927 
1929  my_agent->epmem_timers->hash->start();
1931 
1932  if ( ( sym->common.symbol_type == SYM_CONSTANT_SYMBOL_TYPE ) ||
1933  ( sym->common.symbol_type == INT_CONSTANT_SYMBOL_TYPE ) ||
1934  ( sym->common.symbol_type == FLOAT_CONSTANT_SYMBOL_TYPE ) )
1935  {
1936  if ( ( !sym->common.epmem_hash ) || ( sym->common.epmem_valid != my_agent->epmem_validation ) )
1937  {
1938  sym->common.epmem_hash = NIL;
1939  sym->common.epmem_valid = my_agent->epmem_validation;
1940 
1941  // basic process:
1942  // - search
1943  // - if found, return
1944  // - else, add
1945 
1946  my_agent->epmem_stmts_common->hash_get->bind_int( 1, sym->common.symbol_type );
1947  switch ( sym->common.symbol_type )
1948  {
1950  my_agent->epmem_stmts_common->hash_get->bind_text( 2, static_cast<const char *>( sym->sc.name ) );
1951  break;
1952 
1954  my_agent->epmem_stmts_common->hash_get->bind_int( 2, sym->ic.value );
1955  break;
1956 
1958  my_agent->epmem_stmts_common->hash_get->bind_double( 2, sym->fc.value );
1959  break;
1960  }
1961 
1962  if ( my_agent->epmem_stmts_common->hash_get->execute() == soar_module::row )
1963  {
1964  return_val = static_cast<epmem_hash_id>( my_agent->epmem_stmts_common->hash_get->column_int( 0 ) );
1965  }
1966 
1968 
1969  //
1970 
1971  if ( !return_val && add_on_fail )
1972  {
1973  my_agent->epmem_stmts_common->hash_add->bind_int( 1, sym->common.symbol_type );
1974  switch ( sym->common.symbol_type )
1975  {
1977  my_agent->epmem_stmts_common->hash_add->bind_text( 2, static_cast<const char *>( sym->sc.name ) );
1978  break;
1979 
1981  my_agent->epmem_stmts_common->hash_add->bind_int( 2, sym->ic.value );
1982  break;
1983 
1985  my_agent->epmem_stmts_common->hash_add->bind_double( 2, sym->fc.value );
1986  break;
1987  }
1988 
1990  return_val = static_cast<epmem_hash_id>( my_agent->epmem_db->last_insert_rowid() );
1991  }
1992 
1993  // cache results for later re-use
1994  sym->common.epmem_hash = return_val;
1995  sym->common.epmem_valid = my_agent->epmem_validation;
1996  }
1997 
1998  return_val = sym->common.epmem_hash;
1999  }
2000 
2002  my_agent->epmem_timers->hash->stop();
2004 
2005  return return_val;
2006 }
2007 
2008 
2011 // Storage Functions (epmem::storage)
2014 
2015 void epmem_schedule_promotion( agent* my_agent, Symbol* id )
2016 {
2017  if ( my_agent->epmem_db->get_status() == soar_module::connected )
2018  {
2019  if ( ( id->id.epmem_id != EPMEM_NODEID_BAD ) && ( id->id.epmem_valid == my_agent->epmem_validation ) )
2020  {
2021  symbol_add_ref( id );
2022  my_agent->epmem_promotions->insert( id );
2023  }
2024  }
2025 }
2026 
2027 inline void _epmem_promote_id( agent* my_agent, Symbol* id, epmem_time_id t )
2028 {
2029  // parent_id,letter,num,time_id
2030  my_agent->epmem_stmts_graph->promote_id->bind_int( 1, id->id.epmem_id );
2031  my_agent->epmem_stmts_graph->promote_id->bind_int( 2, static_cast<uint64_t>( id->id.name_letter ) );
2032  my_agent->epmem_stmts_graph->promote_id->bind_int( 3, static_cast<uint64_t>( id->id.name_number ) );
2033  my_agent->epmem_stmts_graph->promote_id->bind_int( 4, t );
2035 }
2036 
2037 // three cases for sharing ids amongst identifiers in two passes:
2038 // 1. value known in phase one (try reservation)
2039 // 2. value unknown in phase one, but known at phase two (try assignment adhering to constraint)
2040 // 3. value unknown in phase one/two (if anything is left, unconstrained assignment)
2041 inline void _epmem_store_level( agent* my_agent, std::queue< Symbol* >& parent_syms, std::queue< epmem_node_id >& parent_ids, tc_number tc, epmem_wme_list::iterator w_b, epmem_wme_list::iterator w_e, epmem_node_id parent_id, epmem_time_id time_counter,
2042  std::map< wme*, epmem_id_reservation* >& id_reservations, std::set< Symbol* >& new_identifiers, std::queue< epmem_node_id >& epmem_node, std::queue< epmem_node_id >& epmem_edge )
2043 {
2044  epmem_wme_list::iterator w_p;
2045  bool value_known_apriori = false;
2046 
2047  // temporal hash
2048  epmem_hash_id my_hash; // attribute
2049  epmem_hash_id my_hash2; // value
2050 
2051  // id repository
2052  epmem_id_pool **my_id_repo;
2053  epmem_id_pool *my_id_repo2;
2054  epmem_id_pool::iterator pool_p;
2055  std::map<wme *, epmem_id_reservation *>::iterator r_p;
2056  epmem_id_reservation *new_id_reservation;
2057 
2058  // identifier recursion
2059  epmem_wme_list* wmes = NULL;
2060  epmem_wme_list::iterator w_p2;
2061  bool good_recurse = false;
2062 
2063  // find WME ID for WMEs whose value is an identifier and has a known epmem id (prevents ordering issues with unknown children)
2064  for ( w_p=w_b; w_p!=w_e; w_p++ )
2065  {
2066  // skip over WMEs already in the system
2067  if ( ( (*w_p)->epmem_id != EPMEM_NODEID_BAD ) && ( (*w_p)->epmem_valid == my_agent->epmem_validation ) )
2068  {
2069  continue;
2070  }
2071 
2072  if ( ( (*w_p)->value->common.symbol_type == IDENTIFIER_SYMBOL_TYPE ) &&
2073  ( ( (*w_p)->value->id.epmem_id != EPMEM_NODEID_BAD ) && ( (*w_p)->value->id.epmem_valid == my_agent->epmem_validation ) ) &&
2074  ( !(*w_p)->value->id.smem_lti ) )
2075  {
2076  // prevent exclusions from being recorded
2077  if ( my_agent->epmem_params->exclusions->in_set( (*w_p)->attr ) )
2078  {
2079  continue;
2080  }
2081 
2082  // if still here, create reservation (case 1)
2083  new_id_reservation = new epmem_id_reservation;
2084  new_id_reservation->my_id = EPMEM_NODEID_BAD;
2085  new_id_reservation->my_pool = NULL;
2086 
2087  if ( (*w_p)->acceptable )
2088  {
2089  new_id_reservation->my_hash = EPMEM_HASH_ACCEPTABLE;
2090  }
2091  else
2092  {
2093  new_id_reservation->my_hash = epmem_temporal_hash( my_agent, (*w_p)->attr );
2094  }
2095 
2096  // try to find appropriate reservation
2097  my_id_repo =& (*(*my_agent->epmem_id_repository)[ parent_id ])[ new_id_reservation->my_hash ];
2098  if ( (*my_id_repo) )
2099  {
2100  for ( pool_p = (*my_id_repo)->begin(); pool_p != (*my_id_repo)->end(); pool_p++ )
2101  {
2102  if ( pool_p->first == (*w_p)->value->id.epmem_id )
2103  {
2104  new_id_reservation->my_id = pool_p->second;
2105  (*my_id_repo)->erase( pool_p );
2106  break;
2107  }
2108  }
2109  }
2110  else
2111  {
2112  // add repository
2113  (*my_id_repo) = new epmem_id_pool;
2114  }
2115 
2116  new_id_reservation->my_pool = (*my_id_repo);
2117  id_reservations[ (*w_p) ] = new_id_reservation;
2118  new_id_reservation = NULL;
2119  }
2120  }
2121 
2122  for ( w_p=w_b; w_p!=w_e; w_p++ )
2123  {
2124  // skip over WMEs already in the system
2125  if ( ( (*w_p)->epmem_id != EPMEM_NODEID_BAD ) && ( (*w_p)->epmem_valid == my_agent->epmem_validation ) )
2126  {
2127  continue;
2128  }
2129 
2130  // prevent exclusions from being recorded
2131  if ( my_agent->epmem_params->exclusions->in_set( (*w_p)->attr ) )
2132  {
2133  continue;
2134  }
2135 
2136  if ( (*w_p)->value->common.symbol_type == IDENTIFIER_SYMBOL_TYPE )
2137  {
2138  (*w_p)->epmem_valid = my_agent->epmem_validation;
2139  (*w_p)->epmem_id = EPMEM_NODEID_BAD;
2140 
2141  my_hash = NIL;
2142  my_id_repo2 = NIL;
2143 
2144  // if the value already has an epmem_id, the WME ID would have already been assigned above (ie. the epmem_id of the VALUE is KNOWN APRIORI [sic])
2145  // however, it's also possible that the value is known but no WME ID is given (eg. (<s> ^foo <a> ^bar <a>)); this is case 2
2146  value_known_apriori = ( ( (*w_p)->value->id.epmem_id != EPMEM_NODEID_BAD ) && ( (*w_p)->value->id.epmem_valid == my_agent->epmem_validation ) );
2147 
2148  // if long-term identifier as value, special processing (we may need to promote, we don't add to/take from any pools)
2149  if ( (*w_p)->value->id.smem_lti )
2150  {
2151  // find the lti or add new one
2152  if ( !value_known_apriori )
2153  {
2154  (*w_p)->value->id.epmem_id = EPMEM_NODEID_BAD;
2155  (*w_p)->value->id.epmem_valid = my_agent->epmem_validation;
2156 
2157  // try to find
2158  {
2159  my_agent->epmem_stmts_graph->find_lti->bind_int( 1, static_cast<uint64_t>( (*w_p)->value->id.name_letter ) );
2160  my_agent->epmem_stmts_graph->find_lti->bind_int( 2, static_cast<uint64_t>( (*w_p)->value->id.name_number ) );
2161 
2162  if ( my_agent->epmem_stmts_graph->find_lti->execute() == soar_module::row )
2163  {
2164  (*w_p)->value->id.epmem_id = static_cast<epmem_node_id>( my_agent->epmem_stmts_graph->find_lti->column_int( 0 ) );
2165  }
2166 
2167  my_agent->epmem_stmts_graph->find_lti->reinitialize();
2168  }
2169 
2170  // add if necessary
2171  if ( (*w_p)->value->id.epmem_id == EPMEM_NODEID_BAD )
2172  {
2173  (*w_p)->value->id.epmem_id = my_agent->epmem_stats->next_id->get_value();
2174  my_agent->epmem_stats->next_id->set_value( (*w_p)->value->id.epmem_id + 1 );
2175  epmem_set_variable( my_agent, var_next_id, (*w_p)->value->id.epmem_id + 1 );
2176 
2177  // add repository
2178  (*my_agent->epmem_id_repository)[ (*w_p)->value->id.epmem_id ] = new epmem_hashed_id_pool;
2179 
2180  _epmem_promote_id( my_agent, (*w_p)->value, time_counter );
2181  }
2182  }
2183 
2184  // now perform deliberate edge search
2185  // ltis don't use the pools, so we make a direct search in the edge_unique table
2186  // if failure, drop below and use standard channels
2187  {
2188  // get temporal hash
2189  if ( (*w_p)->acceptable )
2190  {
2191  my_hash = EPMEM_HASH_ACCEPTABLE;
2192  }
2193  else
2194  {
2195  my_hash = epmem_temporal_hash( my_agent, (*w_p)->attr );
2196  }
2197 
2198  // q0, w, q1
2199  my_agent->epmem_stmts_graph->find_edge_unique_shared->bind_int( 1, parent_id );
2200  my_agent->epmem_stmts_graph->find_edge_unique_shared->bind_int( 2, my_hash );
2201  my_agent->epmem_stmts_graph->find_edge_unique_shared->bind_int( 3, (*w_p)->value->id.epmem_id );
2202 
2204  {
2205  (*w_p)->epmem_id = my_agent->epmem_stmts_graph->find_edge_unique_shared->column_int( 0 );
2206  }
2207 
2209  }
2210  }
2211  else
2212  {
2213  // in the case of a known value, we already have a reservation (case 1)
2214  if ( value_known_apriori )
2215  {
2216  r_p = id_reservations.find( (*w_p) );
2217 
2218  if ( r_p != id_reservations.end() )
2219  {
2220  // restore reservation info
2221  my_hash = r_p->second->my_hash;
2222  my_id_repo2 = r_p->second->my_pool;
2223 
2224  if ( r_p->second->my_id != EPMEM_NODEID_BAD )
2225  {
2226  (*w_p)->epmem_id = r_p->second->my_id;
2227  (*my_agent->epmem_id_replacement)[ (*w_p)->epmem_id ] = my_id_repo2;
2228  }
2229 
2230  // delete reservation and map entry
2231  delete r_p->second;
2232  id_reservations.erase( r_p );
2233  }
2234  // OR a shared identifier at the same level, in which case we need an exact match (case 2)
2235  else
2236  {
2237  // get temporal hash
2238  if ( (*w_p)->acceptable )
2239  {
2240  my_hash = EPMEM_HASH_ACCEPTABLE;
2241  }
2242  else
2243  {
2244  my_hash = epmem_temporal_hash( my_agent, (*w_p)->attr );
2245  }
2246 
2247  // try to get an id that matches new information
2248  my_id_repo =& (*(*my_agent->epmem_id_repository)[ parent_id ])[ my_hash ];
2249  if ( (*my_id_repo) )
2250  {
2251  if ( !(*my_id_repo)->empty() )
2252  {
2253  for ( pool_p = (*my_id_repo)->begin(); pool_p != (*my_id_repo)->end(); pool_p++ )
2254  {
2255  if ( pool_p->first == (*w_p)->value->id.epmem_id )
2256  {
2257  (*w_p)->epmem_id = pool_p->second;
2258  (*my_id_repo)->erase( pool_p );
2259  (*my_agent->epmem_id_replacement)[ (*w_p)->epmem_id ] = (*my_id_repo);
2260  break;
2261  }
2262  }
2263  }
2264  }
2265  else
2266  {
2267  // add repository
2268  (*my_id_repo) = new epmem_id_pool;
2269  }
2270 
2271  // keep the address for later (used if w->epmem_id was not assigned)
2272  my_id_repo2 = (*my_id_repo);
2273  }
2274  }
2275  // case 3
2276  else
2277  {
2278  // UNKNOWN identifier
2279  new_identifiers.insert( (*w_p)->value );
2280 
2281  // get temporal hash
2282  if ( (*w_p)->acceptable )
2283  {
2284  my_hash = EPMEM_HASH_ACCEPTABLE;
2285  }
2286  else
2287  {
2288  my_hash = epmem_temporal_hash( my_agent, (*w_p)->attr );
2289  }
2290 
2291  // try to find node
2292  my_id_repo =& (*(*my_agent->epmem_id_repository)[ parent_id ])[ my_hash ];
2293  if ( (*my_id_repo) )
2294  {
2295  // if something leftover, try to use it
2296  if ( !(*my_id_repo)->empty() )
2297  {
2298  for (pool_p = (*my_id_repo)->begin(); pool_p != (*my_id_repo)->end(); pool_p++)
2299  {
2300  if ( (*my_agent->epmem_id_ref_counts)[ pool_p->first ]->empty() )
2301  {
2302  (*w_p)->epmem_id = pool_p->second;
2303  (*w_p)->value->id.epmem_id = pool_p->first;
2304  (*w_p)->value->id.epmem_valid = my_agent->epmem_validation;
2305  (*my_id_repo)->erase( pool_p );
2306  (*my_agent->epmem_id_replacement)[ (*w_p)->epmem_id ] = (*my_id_repo);
2307  break;
2308  }
2309  }
2310  }
2311  }
2312  else
2313  {
2314  // add repository
2315  (*my_id_repo) = new epmem_id_pool;
2316  }
2317 
2318  // keep the address for later (used if w->epmem_id was not assgined)
2319  my_id_repo2 = (*my_id_repo);
2320  }
2321  }
2322 
2323  // add wme if no success above
2324  if ( (*w_p)->epmem_id == EPMEM_NODEID_BAD )
2325  {
2326  // can't use value_known_apriori, since value may have been assigned (lti, id repository via case 3)
2327  if ( ( (*w_p)->value->id.epmem_id == EPMEM_NODEID_BAD ) || ( (*w_p)->value->id.epmem_valid != my_agent->epmem_validation ) )
2328  {
2329  // update next id
2330  (*w_p)->value->id.epmem_id = my_agent->epmem_stats->next_id->get_value();
2331  (*w_p)->value->id.epmem_valid = my_agent->epmem_validation;
2332  my_agent->epmem_stats->next_id->set_value( (*w_p)->value->id.epmem_id + 1 );
2333  epmem_set_variable( my_agent, var_next_id, (*w_p)->value->id.epmem_id + 1 );
2334 
2335  // add repository for possible future children
2336  (*my_agent->epmem_id_repository)[ (*w_p)->value->id.epmem_id ] = new epmem_hashed_id_pool;
2337 
2338  // add ref set
2340  (*my_agent->epmem_id_ref_counts)[ (*w_p)->value->id.epmem_id ] = new epmem_wme_set( std::less< wme* >(), soar_module::soar_memory_pool_allocator< wme* >( my_agent ) );
2341 #else
2342  (*my_agent->epmem_id_ref_counts)[ (*w_p)->value->id.epmem_id ] = new epmem_wme_set();
2343 #endif
2344  }
2345 
2346  // insert (q0,w,q1)
2347  my_agent->epmem_stmts_graph->add_edge_unique->bind_int( 1, parent_id );
2348  my_agent->epmem_stmts_graph->add_edge_unique->bind_int( 2, my_hash );
2349  my_agent->epmem_stmts_graph->add_edge_unique->bind_int( 3, (*w_p)->value->id.epmem_id );
2350  my_agent->epmem_stmts_graph->add_edge_unique->bind_int( 4, LLONG_MAX );
2352 
2353  (*w_p)->epmem_id = static_cast<epmem_node_id>( my_agent->epmem_db->last_insert_rowid() );
2354 
2355  if ( !(*w_p)->value->id.smem_lti )
2356  {
2357  // replace the epmem_id and wme id in the right place
2358  (*my_agent->epmem_id_replacement)[ (*w_p)->epmem_id ] = my_id_repo2;
2359  }
2360 
2361  // new nodes definitely start
2362  epmem_edge.push( (*w_p)->epmem_id );
2363  my_agent->epmem_edge_mins->push_back( time_counter );
2364  my_agent->epmem_edge_maxes->push_back( false );
2365  }
2366  else
2367  {
2368  // definitely don't remove
2369  (*my_agent->epmem_edge_removals)[ (*w_p)->epmem_id ] = false;
2370 
2371  // we add ONLY if the last thing we did was remove
2372  if ( (*my_agent->epmem_edge_maxes)[ (*w_p)->epmem_id - 1 ] )
2373  {
2374  epmem_edge.push( (*w_p)->epmem_id );
2375  (*my_agent->epmem_edge_maxes)[ (*w_p)->epmem_id - 1 ] = false;
2376  }
2377  }
2378 
2379  // at this point we have successfully added a new wme
2380  // whose value is an identifier. IF the value was
2381  // unknown at the beginning of this episode, then we need
2382  // to update its ref count for each WME added (thereby catching
2383  // up with ref counts that would have been accumulated via wme adds)
2384  if ( new_identifiers.find( (*w_p)->value ) != new_identifiers.end() )
2385  {
2386  (*my_agent->epmem_id_ref_counts)[ (*w_p)->value->id.epmem_id ]->insert( (*w_p) );
2387  }
2388 
2389  // if the value has not been iterated over, continue to augmentations
2390  if ( (*w_p)->value->id.tc_num != tc )
2391  {
2392  parent_syms.push( (*w_p)->value );
2393  parent_ids.push( (*w_p)->value->id.epmem_id );
2394  }
2395  }
2396  else
2397  {
2398  // have we seen this node in this database?
2399  if ( ( (*w_p)->epmem_id == EPMEM_NODEID_BAD ) || ( (*w_p)->epmem_valid != my_agent->epmem_validation ) )
2400  {
2401  (*w_p)->epmem_id = EPMEM_NODEID_BAD;
2402  (*w_p)->epmem_valid = my_agent->epmem_validation;
2403 
2404  my_hash = epmem_temporal_hash( my_agent, (*w_p)->attr );
2405  my_hash2 = epmem_temporal_hash( my_agent, (*w_p)->value );
2406 
2407  // try to get node id
2408  {
2409  // parent_id=? AND attr=? AND value=?
2410  my_agent->epmem_stmts_graph->find_node_unique->bind_int( 1, parent_id );
2411  my_agent->epmem_stmts_graph->find_node_unique->bind_int( 2, my_hash );
2412  my_agent->epmem_stmts_graph->find_node_unique->bind_int( 3, my_hash2 );
2413 
2415  {
2416  (*w_p)->epmem_id = my_agent->epmem_stmts_graph->find_node_unique->column_int( 0 );
2417  }
2418 
2420  }
2421 
2422  // act depending on new/existing feature
2423  if ( (*w_p)->epmem_id == EPMEM_NODEID_BAD )
2424  {
2425  // insert (parent_id,attr,value)
2426  my_agent->epmem_stmts_graph->add_node_unique->bind_int( 1, parent_id );
2427  my_agent->epmem_stmts_graph->add_node_unique->bind_int( 2, my_hash );
2428  my_agent->epmem_stmts_graph->add_node_unique->bind_int( 3, my_hash2 );
2430 
2431  (*w_p)->epmem_id = (epmem_node_id) my_agent->epmem_db->last_insert_rowid();
2432 
2433  // new nodes definitely start
2434  epmem_node.push( (*w_p)->epmem_id );
2435  my_agent->epmem_node_mins->push_back( time_counter );
2436  my_agent->epmem_node_maxes->push_back( false );
2437  }
2438  else
2439  {
2440  // definitely don't remove
2441  (*my_agent->epmem_node_removals)[ (*w_p)->epmem_id ] = false;
2442 
2443  // add ONLY if the last thing we did was add
2444  if ( (*my_agent->epmem_node_maxes)[ (*w_p)->epmem_id - 1 ] )
2445  {
2446  epmem_node.push( (*w_p)->epmem_id );
2447  (*my_agent->epmem_node_maxes)[ (*w_p)->epmem_id - 1 ] = false;
2448  }
2449  }
2450  }
2451  }
2452  }
2453 }
2454 
2455 void epmem_new_episode( agent *my_agent )
2456 {
2457  // if this is the first episode, initialize db components
2458  if ( my_agent->epmem_db->get_status() == soar_module::disconnected )
2459  {
2460  epmem_init_db( my_agent );
2461  }
2462 
2463  // add the episode only if db is properly initialized
2464  if ( my_agent->epmem_db->get_status() != soar_module::connected )
2465  {
2466  return;
2467  }
2468 
2470  my_agent->epmem_timers->storage->start();
2472 
2473  epmem_time_id time_counter = my_agent->epmem_stats->time->get_value();
2474 
2475  // provide trace output
2476  if ( my_agent->sysparams[ TRACE_EPMEM_SYSPARAM ] )
2477  {
2478  char buf[256];
2479 
2480  SNPRINTF( buf, 254, "NEW EPISODE: %ld", static_cast<long int>(time_counter) );
2481 
2482  print( my_agent, buf );
2483  xml_generate_warning( my_agent, buf );
2484  }
2485 
2486  // perform storage
2487  {
2488  // seen nodes (non-identifiers) and edges (identifiers)
2489  std::queue<epmem_node_id> epmem_node;
2490  std::queue<epmem_node_id> epmem_edge;
2491 
2492  // walk appropriate levels
2493  {
2494  // prevents infinite loops
2495  tc_number tc = get_new_tc_number( my_agent );
2496 
2497  // children of the current identifier
2498  epmem_wme_list* wmes = NULL;
2499 
2500  // breadth first search state
2501  std::queue< Symbol* > parent_syms;
2502  Symbol* parent_sym = NULL;
2503  std::queue< epmem_node_id > parent_ids;
2504  epmem_node_id parent_id;
2505 
2506  // cross-level information
2507  std::map< wme*, epmem_id_reservation* > id_reservations;
2508  std::set< Symbol* > new_identifiers;
2509 
2510  // start with new WMEs attached to known identifiers
2511  for ( epmem_symbol_set::iterator id_p = my_agent->epmem_wme_adds->begin(); id_p != my_agent->epmem_wme_adds->end(); id_p++ )
2512  {
2513  // make sure the WME is valid
2514  // it can be invalid if a child WME was added, but then the parent was removed, setting the epmem_id to EPMEM_NODEID_BAD
2515  if ((*id_p)->id.epmem_id != EPMEM_NODEID_BAD) {
2516  parent_syms.push( (*id_p) );
2517  parent_ids.push( (*id_p)->id.epmem_id );
2518  while ( !parent_syms.empty() )
2519  {
2520  parent_sym = parent_syms.front();
2521  parent_syms.pop();
2522  parent_id = parent_ids.front();
2523  parent_ids.pop();
2524  wmes = epmem_get_augs_of_id( parent_sym, tc );
2525  if ( ! wmes->empty() )
2526  {
2527  _epmem_store_level( my_agent, parent_syms, parent_ids, tc, wmes->begin(), wmes->end(), parent_id, time_counter, id_reservations, new_identifiers, epmem_node, epmem_edge );
2528  }
2529  delete wmes;
2530  }
2531  }
2532  }
2533  }
2534 
2535  // all inserts
2536  {
2537  epmem_node_id *temp_node;
2538 
2539 #ifdef EPMEM_EXPERIMENT
2540  epmem_dc_interval_inserts = epmem_node.size() + epmem_edge.size();
2541 #endif
2542 
2543  // nodes
2544  while ( !epmem_node.empty() )
2545  {
2546  temp_node =& epmem_node.front();
2547 
2548  // add NOW entry
2549  // id = ?, start = ?
2550  my_agent->epmem_stmts_graph->add_node_now->bind_int( 1, (*temp_node) );
2551  my_agent->epmem_stmts_graph->add_node_now->bind_int( 2, time_counter );
2553 
2554  // update min
2555  (*my_agent->epmem_node_mins)[ (*temp_node) - 1 ] = time_counter;
2556 
2557  epmem_node.pop();
2558  }
2559 
2560  // edges
2561  while ( !epmem_edge.empty() )
2562  {
2563  temp_node =& epmem_edge.front();
2564 
2565  // add NOW entry
2566  // id = ?, start = ?
2567  my_agent->epmem_stmts_graph->add_edge_now->bind_int( 1, (*temp_node) );
2568  my_agent->epmem_stmts_graph->add_edge_now->bind_int( 2, time_counter );
2570 
2571  // update min
2572  (*my_agent->epmem_edge_mins)[ (*temp_node) - 1 ] = time_counter;
2573 
2574  my_agent->epmem_stmts_graph->update_edge_unique_last->bind_int( 1, LLONG_MAX );
2575  my_agent->epmem_stmts_graph->update_edge_unique_last->bind_int( 2, *temp_node );
2577 
2578  epmem_edge.pop();
2579  }
2580  }
2581 
2582  // all removals
2583  {
2584  epmem_id_removal_map::iterator r;
2585  epmem_time_id range_start;
2586  epmem_time_id range_end;
2587 
2588 #ifdef EPMEM_EXPERIMENT
2589  epmem_dc_interval_removes = 0;
2590 #endif
2591 
2592  // nodes
2593  r = my_agent->epmem_node_removals->begin();
2594  while ( r != my_agent->epmem_node_removals->end() )
2595  {
2596  if ( r->second )
2597  {
2598 #ifdef EPMEM_EXPERIMENT
2599  epmem_dc_interval_removes++;
2600 #endif
2601 
2602  // remove NOW entry
2603  // id = ?
2604  my_agent->epmem_stmts_graph->delete_node_now->bind_int( 1, r->first );
2606 
2607  range_start = (*my_agent->epmem_node_mins)[ r->first - 1 ];
2608  range_end = ( time_counter - 1 );
2609 
2610  // point (id, start)
2611  if ( range_start == range_end )
2612  {
2613  my_agent->epmem_stmts_graph->add_node_point->bind_int( 1, r->first );
2614  my_agent->epmem_stmts_graph->add_node_point->bind_int( 2, range_start );
2616  }
2617  // node
2618  else
2619  {
2620  epmem_rit_insert_interval( my_agent, range_start, range_end, r->first, &( my_agent->epmem_rit_state_graph[ EPMEM_RIT_STATE_NODE ] ) );
2621  }
2622 
2623  // update max
2624  (*my_agent->epmem_node_maxes)[ r->first - 1 ] = true;
2625  }
2626 
2627  r++;
2628  }
2629  my_agent->epmem_node_removals->clear();
2630 
2631  // edges
2632  r = my_agent->epmem_edge_removals->begin();
2633  while ( r != my_agent->epmem_edge_removals->end() )
2634  {
2635  if ( r->second )
2636  {
2637 #ifdef EPMEM_EXPERIMENT
2638  epmem_dc_interval_removes++;
2639 #endif
2640 
2641  // remove NOW entry
2642  // id = ?
2643  my_agent->epmem_stmts_graph->delete_edge_now->bind_int( 1, r->first );
2645 
2646  range_start = (*my_agent->epmem_edge_mins)[ r->first - 1 ];
2647  range_end = ( time_counter - 1 );
2648 
2649  my_agent->epmem_stmts_graph->update_edge_unique_last->bind_int( 1, range_end );
2650  my_agent->epmem_stmts_graph->update_edge_unique_last->bind_int( 2, r->first );
2652  // point (id, start)
2653  if ( range_start == range_end )
2654  {
2655  my_agent->epmem_stmts_graph->add_edge_point->bind_int( 1, r->first );
2656  my_agent->epmem_stmts_graph->add_edge_point->bind_int( 2, range_start );
2658  }
2659  // node
2660  else
2661  {
2662  epmem_rit_insert_interval( my_agent, range_start, range_end, r->first, &( my_agent->epmem_rit_state_graph[ EPMEM_RIT_STATE_EDGE ] ) );
2663  }
2664 
2665  // update max
2666  (*my_agent->epmem_edge_maxes)[ r->first - 1 ] = true;
2667  }
2668 
2669  r++;
2670  }
2671  my_agent->epmem_edge_removals->clear();
2672  }
2673 
2674  // all in-place lti promotions
2675  {
2676  for ( epmem_symbol_set::iterator p_it=my_agent->epmem_promotions->begin(); p_it!=my_agent->epmem_promotions->end(); p_it++ )
2677  {
2678  if ( ( (*p_it)->id.smem_time_id == time_counter ) && ( (*p_it)->id.smem_valid == my_agent->epmem_validation ) )
2679  {
2680  _epmem_promote_id( my_agent, (*p_it), time_counter );
2681  }
2682 
2683  symbol_remove_ref( my_agent, (*p_it) );
2684  }
2685  my_agent->epmem_promotions->clear();
2686  }
2687 
2688  // add the time id to the times table
2689  my_agent->epmem_stmts_graph->add_time->bind_int( 1, time_counter );
2691 
2692  my_agent->epmem_stats->time->set_value( time_counter + 1 );
2693 
2694  // update time wme on all states
2695  {
2696  Symbol* state = my_agent->bottom_goal;
2697  Symbol* my_time_sym = make_int_constant( my_agent, time_counter + 1 );
2698 
2699  while ( state != NULL )
2700  {
2701  if ( state->id.epmem_time_wme != NIL )
2702  {
2703  soar_module::remove_module_wme( my_agent, state->id.epmem_time_wme );
2704  }
2705 
2706  state->id.epmem_time_wme = soar_module::add_module_wme( my_agent, state->id.epmem_header, my_agent->epmem_sym_present_id, my_time_sym );
2707 
2708  state = state->id.higher_goal;
2709  }
2710 
2711  symbol_remove_ref( my_agent, my_time_sym );
2712  }
2713 
2714  // clear add/remove maps
2715  {
2716  my_agent->epmem_wme_adds->clear();
2717  }
2718  }
2719 
2721  my_agent->epmem_timers->storage->stop();
2723 }
2724 
2727 // Non-Cue-Based Retrieval Functions (epmem::ncb)
2728 //
2729 // NCB retrievals occur when you know the episode you
2730 // want to retrieve. It is the process of converting
2731 // the database representation to WMEs in working
2732 // memory.
2733 //
2734 // This occurs at the end of a cue-based query, or
2735 // in response to a retrieve/next/previous command.
2736 //
2739 
2740 /***************************************************************************
2741  * Function : epmem_valid_episode
2742  * Author : Nate Derbinsky
2743  * Notes : Returns true if the temporal id is valid
2744  **************************************************************************/
2745 bool epmem_valid_episode( agent *my_agent, epmem_time_id memory_id )
2746 {
2747  bool return_val = false;
2748 
2749  {
2751 
2752  my_q->bind_int( 1, memory_id );
2753  my_q->execute();
2754  return_val = ( my_q->column_int( 0 ) > 0 );
2755  my_q->reinitialize();
2756  }
2757 
2758  return return_val;
2759 }
2760 
2761 inline void _epmem_install_id_wme( agent* my_agent, Symbol* parent, Symbol* attr, std::map< epmem_node_id, std::pair< Symbol*, bool > >* ids, epmem_node_id q1, bool val_is_short_term, char val_letter, uint64_t val_num, epmem_id_mapping* id_record, soar_module::symbol_triple_list& retrieval_wmes )
2762 {
2763  std::map< epmem_node_id, std::pair< Symbol*, bool > >::iterator id_p = ids->find( q1 );
2764  bool existing_identifier = ( id_p != ids->end() );
2765 
2766  if ( val_is_short_term )
2767  {
2768  if ( !existing_identifier )
2769  {
2770  id_p = ids->insert( std::make_pair< epmem_node_id, std::pair< Symbol*, bool > >( q1, std::make_pair< Symbol*, bool >( make_new_identifier( my_agent, ( ( attr->common.symbol_type == SYM_CONSTANT_SYMBOL_TYPE )?( attr->sc.name[0] ):('E') ), parent->id.level ), true ) ) ).first;
2771  if ( id_record )
2772  {
2773  epmem_id_mapping::iterator rec_p = id_record->find( q1 );
2774  if ( rec_p != id_record->end() )
2775  {
2776  rec_p->second = id_p->second.first;
2777  }
2778  }
2779  }
2780 
2781  epmem_buffer_add_wme( retrieval_wmes, parent, attr, id_p->second.first );
2782 
2783  if ( !existing_identifier )
2784  {
2785  symbol_remove_ref( my_agent, id_p->second.first );
2786  }
2787  }
2788  else
2789  {
2790  if ( existing_identifier )
2791  {
2792  epmem_buffer_add_wme( retrieval_wmes, parent, attr, id_p->second.first );
2793  }
2794  else
2795  {
2796  Symbol* value = smem_lti_soar_make( my_agent, smem_lti_get_id( my_agent, val_letter, val_num ), val_letter, val_num, parent->id.level );
2797 
2798  if ( id_record )
2799  {
2800  epmem_id_mapping::iterator rec_p = id_record->find( q1 );
2801  if ( rec_p != id_record->end() )
2802  {
2803  rec_p->second = value;
2804  }
2805  }
2806 
2807  epmem_buffer_add_wme( retrieval_wmes, parent, attr, value );
2808  symbol_remove_ref( my_agent, value );
2809 
2810  ids->insert( std::make_pair< epmem_node_id, std::pair< Symbol*, bool > >( q1, std::make_pair< Symbol*, bool >( value, !( ( value->id.impasse_wmes ) || ( value->id.input_wmes ) || ( value->id.slots ) ) ) ) );
2811  }
2812  }
2813 }
2814 
2815 /***************************************************************************
2816  * Function : epmem_install_memory
2817  * Author : Nate Derbinsky
2818  * Notes : Reconstructs an episode in working memory.
2819  *
2820  * Use RIT to collect appropriate ranges. Then
2821  * combine with NOW and POINT. Merge with unique
2822  * to get all the data necessary to create WMEs.
2823  *
2824  * The id_record parameter is only used in the case
2825  * that the graph-match has a match and creates
2826  * a mapping of identifiers that should be recorded
2827  * during reconstruction.
2828  **************************************************************************/
2829 void epmem_install_memory( agent *my_agent, Symbol *state, epmem_time_id memory_id, soar_module::symbol_triple_list& meta_wmes, soar_module::symbol_triple_list& retrieval_wmes, epmem_id_mapping *id_record = NULL )
2830 {
2832  my_agent->epmem_timers->ncb_retrieval->start();
2834 
2835  // get the ^result header for this state
2836  Symbol *result_header = state->id.epmem_result_header;
2837 
2838  // initialize stat
2839  int64_t num_wmes = 0;
2840  my_agent->epmem_stats->ncb_wmes->set_value( num_wmes );
2841 
2842  // if no memory, say so
2843  if ( ( memory_id == EPMEM_MEMID_NONE ) ||
2844  !epmem_valid_episode( my_agent, memory_id ) )
2845  {
2846  epmem_buffer_add_wme( meta_wmes, result_header, my_agent->epmem_sym_retrieved, my_agent->epmem_sym_no_memory );
2848 
2850  my_agent->epmem_timers->ncb_retrieval->stop();
2852 
2853  return;
2854  }
2855 
2856  // remember this as the last memory installed
2857  state->id.epmem_info->last_memory = memory_id;
2858 
2859  // create a new ^retrieved header for this result
2860  Symbol *retrieved_header;
2861  retrieved_header = make_new_identifier( my_agent, 'R', result_header->id.level );
2862  if ( id_record )
2863  {
2864  (*id_record)[ EPMEM_NODEID_ROOT ] = retrieved_header;
2865  }
2866 
2867  epmem_buffer_add_wme( meta_wmes, result_header, my_agent->epmem_sym_retrieved, retrieved_header );
2868  symbol_remove_ref( my_agent, retrieved_header );
2869 
2870  // add *-id wme's
2871  {
2872  Symbol *my_meta;
2873 
2874  my_meta = make_int_constant( my_agent, static_cast<int64_t>( memory_id ) );
2875  epmem_buffer_add_wme( meta_wmes, result_header, my_agent->epmem_sym_memory_id, my_meta );
2876  symbol_remove_ref( my_agent, my_meta );
2877 
2878  my_meta = make_int_constant( my_agent, static_cast<int64_t>( my_agent->epmem_stats->time->get_value() ) );
2879  epmem_buffer_add_wme( meta_wmes, result_header, my_agent->epmem_sym_present_id, my_meta );
2880  symbol_remove_ref( my_agent, my_meta );
2881  }
2882 
2883  // install memory
2884  {
2885  // Big picture: create identifier skeleton, then hang non-identifers
2886  //
2887  // Because of shared WMEs at different levels of the storage breadth-first search,
2888  // there is the possibility that the unique database id of an identifier can be
2889  // greater than that of its parent. Because the retrieval query sorts by
2890  // unique id ascending, it is thus possible to have an "orphan" - a child with
2891  // no current parent. We keep track of orphans and add them later, hoping their
2892  // parents have shown up. I *suppose* there could be a really evil case in which
2893  // the ordering of the unique ids is exactly opposite of their insertion order.
2894  // I just hope this isn't a common case...
2895 
2896  // shared identifier lookup table
2897  std::map< epmem_node_id, std::pair< Symbol*, bool > > ids;
2898  bool dont_abide_by_ids_second = ( my_agent->epmem_params->merge->get_value() == epmem_param_container::merge_add );
2899 
2900  // symbols used to create WMEs
2901  Symbol *attr = NULL;
2902 
2903  // lookup query
2905 
2906  // initialize the lookup table
2907  ids[ EPMEM_NODEID_ROOT ] = std::make_pair< Symbol*, bool >( retrieved_header, true );
2908 
2909  // first identifiers (i.e. reconstruct)
2910  my_q = my_agent->epmem_stmts_graph->get_edges;
2911  {
2912  // relates to finite automata: q1 = d(q0, w)
2913  epmem_node_id q0; // id
2914  epmem_node_id q1; // attribute
2915  int64_t w_type; // we support any constant attribute symbol
2916 
2917  bool val_is_short_term = false;
2918  char val_letter = NIL;
2919  int64_t val_num = NIL;
2920 
2921  // used to lookup shared identifiers
2922  // the bool in the pair refers to if children are allowed on this id (re: lti)
2923  std::map< epmem_node_id, std::pair< Symbol*, bool> >::iterator id_p;
2924 
2925  // orphaned children
2926  std::queue< epmem_edge* > orphans;
2927  epmem_edge *orphan;
2928 
2929  epmem_rit_prep_left_right( my_agent, memory_id, memory_id, &( my_agent->epmem_rit_state_graph[ EPMEM_RIT_STATE_EDGE ] ) );
2930 
2931  my_q->bind_int( 1, memory_id );
2932  my_q->bind_int( 2, memory_id );
2933  my_q->bind_int( 3, memory_id );
2934  my_q->bind_int( 4, memory_id );
2935  my_q->bind_int( 5, memory_id );
2936  while ( my_q->execute() == soar_module::row )
2937  {
2938  // q0, w, q1, w_type
2939  q0 = my_q->column_int( 0 );
2940  q1 = my_q->column_int( 2 );
2941  w_type = my_q->column_int( 3 );
2942 
2943  switch ( w_type )
2944  {
2946  attr = make_int_constant( my_agent,my_q->column_int( 1 ) );
2947  break;
2948 
2950  attr = make_float_constant( my_agent, my_q->column_double( 1 ) );
2951  break;
2952 
2954  attr = make_sym_constant( my_agent, const_cast<char *>( reinterpret_cast<const char *>( my_q->column_text( 1 ) ) ) );
2955  break;
2956  }
2957 
2958  // short vs. long-term
2959  val_is_short_term = ( my_q->column_type( 4 ) == soar_module::null_t );
2960  if ( !val_is_short_term )
2961  {
2962  val_letter = static_cast<char>( my_q->column_int( 4 ) );
2963  val_num = static_cast<uint64_t>( my_q->column_int( 5 ) );
2964  }
2965 
2966  // get a reference to the parent
2967  id_p = ids.find( q0 );
2968  if ( id_p != ids.end() )
2969  {
2970  // if existing lti with kids don't touch
2971  if ( dont_abide_by_ids_second || id_p->second.second )
2972  {
2973  _epmem_install_id_wme( my_agent, id_p->second.first, attr, &( ids ), q1, val_is_short_term, val_letter, val_num, id_record, retrieval_wmes );
2974  num_wmes++;
2975  }
2976 
2977  symbol_remove_ref( my_agent, attr );
2978  }
2979  else
2980  {
2981  // out of order
2982  orphan = new epmem_edge;
2983  orphan->q0 = q0;
2984  orphan->w = attr;
2985  orphan->q1 = q1;
2986 
2987  orphan->val_letter = NIL;
2988  orphan->val_num = NIL;
2989 
2990  orphan->val_is_short_term = val_is_short_term;
2991  if ( !val_is_short_term )
2992  {
2993  orphan->val_letter = val_letter;
2994  orphan->val_num = val_num;
2995  }
2996 
2997  orphans.push( orphan );
2998  }
2999  }
3000  my_q->reinitialize();
3001  epmem_rit_clear_left_right( my_agent );
3002 
3003  // take care of any orphans
3004  if ( !orphans.empty() )
3005  {
3006  std::queue<epmem_edge *>::size_type orphans_left;
3007  std::queue<epmem_edge *> still_orphans;
3008 
3009  do
3010  {
3011  orphans_left = orphans.size();
3012 
3013  while ( !orphans.empty() )
3014  {
3015  orphan = orphans.front();
3016  orphans.pop();
3017 
3018  // get a reference to the parent
3019  id_p = ids.find( orphan->q0 );
3020  if ( id_p != ids.end() )
3021  {
3022  if ( dont_abide_by_ids_second || id_p->second.second )
3023  {
3024  _epmem_install_id_wme( my_agent, id_p->second.first, orphan->w, &( ids ), orphan->q1, orphan->val_is_short_term, orphan->val_letter, orphan->val_num, id_record, retrieval_wmes );
3025  num_wmes++;
3026  }
3027 
3028  symbol_remove_ref( my_agent, orphan->w );
3029 
3030  delete orphan;
3031  }
3032  else
3033  {
3034  still_orphans.push( orphan );
3035  }
3036  }
3037 
3038  orphans = still_orphans;
3039  while ( !still_orphans.empty() )
3040  {
3041  still_orphans.pop();
3042  }
3043 
3044  } while ( ( !orphans.empty() ) && ( orphans_left != orphans.size() ) );
3045 
3046  while ( !orphans.empty() )
3047  {
3048  orphan = orphans.front();
3049  orphans.pop();
3050 
3051  symbol_remove_ref( my_agent, orphan->w );
3052 
3053  delete orphan;
3054  }
3055  }
3056  }
3057 
3058  // then node_unique
3059  my_q = my_agent->epmem_stmts_graph->get_nodes;
3060  {
3061  epmem_node_id child_id;
3062  epmem_node_id parent_id;
3063  int64_t attr_type;
3064  int64_t value_type;
3065 
3066  std::pair< Symbol*, bool > parent;
3067  Symbol *value = NULL;
3068 
3069  epmem_rit_prep_left_right( my_agent, memory_id, memory_id, &( my_agent->epmem_rit_state_graph[ EPMEM_RIT_STATE_NODE ] ) );
3070 
3071  my_q->bind_int( 1, memory_id );
3072  my_q->bind_int( 2, memory_id );
3073  my_q->bind_int( 3, memory_id );
3074  my_q->bind_int( 4, memory_id );
3075  while ( my_q->execute() == soar_module::row )
3076  {
3077  // f.child_id, f.parent_id, f.name, f.value, f.attr_type, f.value_type
3078  child_id = my_q->column_int( 0 );
3079  parent_id = my_q->column_int( 1 );
3080  attr_type = my_q->column_int( 4 );
3081  value_type = my_q->column_int( 5 );
3082 
3083  // get a reference to the parent
3084  parent = ids[ parent_id ];
3085 
3086  if ( dont_abide_by_ids_second || parent.second )
3087  {
3088  // make a symbol to represent the attribute
3089  switch ( attr_type )
3090  {
3092  attr = make_int_constant( my_agent, my_q->column_int( 2 ) );
3093  break;
3094 
3096  attr = make_float_constant( my_agent, my_q->column_double( 2 ) );
3097  break;
3098 
3100  attr = make_sym_constant( my_agent, const_cast<char *>( reinterpret_cast<const char *>( my_q->column_text( 2 ) ) ) );
3101  break;
3102  }
3103 
3104  // make a symbol to represent the value
3105  switch ( value_type )
3106  {
3108  value = make_int_constant( my_agent,my_q->column_int( 3 ) );
3109  break;
3110 
3112  value = make_float_constant( my_agent, my_q->column_double( 3 ) );
3113  break;
3114 
3116  value = make_sym_constant( my_agent, const_cast<char *>( (const char *) my_q->column_text( 3 ) ) );
3117  break;
3118  }
3119 
3120  epmem_buffer_add_wme( retrieval_wmes, parent.first, attr, value );
3121  num_wmes++;
3122 
3123  symbol_remove_ref( my_agent, attr );
3124  symbol_remove_ref( my_agent, value );
3125  }
3126  }
3127  my_q->reinitialize();
3128  epmem_rit_clear_left_right( my_agent );
3129  }
3130  }
3131 
3132  // adjust stat
3133  my_agent->epmem_stats->ncb_wmes->set_value( num_wmes );
3134 
3136  my_agent->epmem_timers->ncb_retrieval->stop();
3138 }
3139 
3140 /***************************************************************************
3141  * Function : epmem_next_episode
3142  * Author : Nate Derbinsky
3143  * Notes : Returns the next valid temporal id. This is really
3144  * only an issue if you implement episode dynamics like
3145  * forgetting.
3146  **************************************************************************/
3148 {
3150  my_agent->epmem_timers->next->start();
3152 
3153  epmem_time_id return_val = EPMEM_MEMID_NONE;
3154 
3155  if ( memory_id != EPMEM_MEMID_NONE )
3156  {
3158  my_q->bind_int( 1, memory_id );
3159  if ( my_q->execute() == soar_module::row )
3160  {
3161  return_val = (epmem_time_id) my_q->column_int( 0 );
3162  }
3163 
3164  my_q->reinitialize();
3165  }
3166 
3168  my_agent->epmem_timers->next->stop();
3170 
3171  return return_val;
3172 }
3173 
3174 /***************************************************************************
3175  * Function : epmem_previous_episode
3176  * Author : Nate Derbinsky
3177  * Notes : Returns the last valid temporal id. This is really
3178  * only an issue if you implement episode dynamics like
3179  * forgetting.
3180  **************************************************************************/
3182 {
3184  my_agent->epmem_timers->prev->start();
3186 
3187  epmem_time_id return_val = EPMEM_MEMID_NONE;
3188 
3189  if ( memory_id != EPMEM_MEMID_NONE )
3190  {
3192  my_q->bind_int( 1, memory_id );
3193  if ( my_q->execute() == soar_module::row )
3194  {
3195  return_val = (epmem_time_id) my_q->column_int( 0 );
3196  }
3197 
3198  my_q->reinitialize();
3199  }
3200 
3202  my_agent->epmem_timers->prev->stop();
3204 
3205  return return_val;
3206 }
3207 
3208 
3211 // Cue-Based Retrieval (epmem::cbr)
3212 //
3213 // Cue-based retrievals are searches in response to
3214 // queries and/or neg-queries.
3215 //
3216 // All functions below implement John/Andy's range search
3217 // algorithm (see Andy's thesis). The primary insight
3218 // is to only deal with changes. In this case, change
3219 // occurs at the end points of ranges of node occurrence.
3220 //
3221 // The implementations below share a common theme:
3222 // 1) identify wmes in the cue
3223 // 2) get pointers to ALL b-tree leaves
3224 // associated with sorted occurrence-endpoints
3225 // of these wmes (ranges, points, now)
3226 // 3) step through the leaves according to the range
3227 // search algorithm
3228 //
3229 // In the Working Memory Tree, the occurrence of a leaf
3230 // node is tantamount to the occurrence of the path to
3231 // the leaf node (since there are no shared identifiers).
3232 // However, when we add graph functionality, path is
3233 // important. Moreover, identifiers that "blink" have
3234 // ambiguous identities over time. Thus I introduced
3235 // the Disjunctive Normal Form (DNF) graph.
3236 //
3237 // The primary insight of the DNF graph is that paths to
3238 // leaf nodes can be written as the disjunction of the
3239 // conjunction of paths.
3240 //
3241 // Metaphor: consider that a human child's lineage is
3242 // in question (perhaps for purposes of estate). We
3243 // are unsure as to the child's grandfather. The grand-
3244 // father can be either gA or gB. If it is gA, then the
3245 // father is absolutely fA (otherwise fB). So the child
3246 // could exist as (where cX is child with lineage X):
3247 //
3248 // (gA ^ fA ^ cA) \/ (gB ^ fB ^ cB)
3249 //
3250 // Note that due to family... irregularities
3251 // (i.e. men sleeping around), a parent might contribute
3252 // to multiple family lines. Thus gX could exist in
3253 // multiple clauses. However, due to well-enforced
3254 // incest laws (i.e. we only support acyclic graph cues),
3255 // an individual can only occur once within a lineage/clause.
3256 //
3257 // We have a "match" (i.e. identify the child's lineage)
3258 // only if all literals are "on" in a path of
3259 // lineage. Thus, our task is to efficiently track DNF
3260 // satisfaction while flipping on/off a single literal
3261 // (which may exist in multiple clauses).
3262 //
3263 // The DNF graph implements this intuition efficiently by
3264 // (say it with me) only processing changes! First we
3265 // construct the graph by creating "literals" (gA, fA, etc)
3266 // and maintaining parent-child relationships (gA connects
3267 // to fA which connects to cA). Leaf literals have no
3268 // children, but are associated with a "match." Each match
3269 // has a cardinality count (positive/negative 1 depending on
3270 // query vs. neg-query) and a WMA value (weighting).
3271 //
3272 // We then connect the b-tree pointers from above with
3273 // each of the literals. It is possible that a query
3274 // can serve multiple literals, so we gain from sharing.
3275 //
3276 // Nodes within the DNF Graph need only save a "count":
3277 // zero means neither it nor its lineage to date is
3278 // satisfied, one means either its lineage or it is
3279 // satisfied, two means it and its lineage is satisfied.
3280 //
3281 // The range search algorithm is simply walking (in-order)
3282 // the parallel b-tree pointers. When we get to an endpoint,
3283 // we appropriately turn on/off all directly associated
3284 // literals. Based upon the current state of the literals,
3285 // we may need to propagate changes to children.
3286 //
3287 // If propogation reaches and changes a match, we alter the
3288 // current episode's cardinality/score. Thus we achieve
3289 // the Soar mantra of only processing changes!
3290 //
3293 
3295 // Justin's Stuff
3297 
3298 #define QUERY_DEBUG 0
3299 
3301  //std::map<epmem_node_id, std::string> tsh;
3302  std::cout << std::endl;
3303  std::cout << "digraph {" << std::endl;
3304  std::cout << "node [style=\"filled\"];" << std::endl;
3305  // LITERALS
3306  std::cout << "subgraph cluster_literals {" << std::endl;
3307  std::cout << "node [fillcolor=\"#0084D1\"];" << std::endl;
3308  for (epmem_wme_literal_map::iterator lit_iter = literals.begin(); lit_iter != literals.end(); lit_iter++) {
3309  epmem_literal* literal = (*lit_iter).second;
3310  if (literal->id_sym) {
3311  std::cout << "\"" << literal->value_sym << "\" [";
3312  if (literal->q1 == EPMEM_NODEID_BAD) {
3313  std::cout << "label=\"" << literal->value_sym << "\"";
3314  } else {
3315  std::cout << "label=\"" << literal->q1 << "\"";
3316  }
3317  if (!literal->value_is_id) {
3318  std::cout << ", shape=\"rect\"";
3319  }
3320  if (literal->matches.size() == 0) {
3321  std::cout << ", penwidth=\"2.0\"";
3322  }
3323  if (literal->is_neg_q) {
3324  std::cout << ", fillcolor=\"#C5000B\"";
3325  }
3326  std::cout << "];" << std::endl;
3327  std::cout << "\"" << literal->id_sym << "\" -> \"" << literal->value_sym << "\" [label=\"";
3328  if (literal->w == EPMEM_NODEID_BAD) {
3329  std::cout << "?";
3330  } else {
3331  std::cout << literal->w;
3332  }
3333  std::cout << "\\n" << literal << "\"];" << std::endl;
3334  }
3335  }
3336  std::cout << "};" << std::endl;
3337  // NODES / NODE->NODE
3338  std::cout << "subgraph cluster_uedges{" << std::endl;
3339  std::cout << "node [fillcolor=\"#FFD320\"];" << std::endl;
3340  for (int type = EPMEM_RIT_STATE_NODE; type <= EPMEM_RIT_STATE_EDGE; type++) {
3341  epmem_triple_uedge_map* uedge_cache = &uedge_caches[type];
3342  for (epmem_triple_uedge_map::iterator uedge_iter = uedge_cache->begin(); uedge_iter != uedge_cache->end(); uedge_iter++) {
3343  epmem_triple triple = (*uedge_iter).first;
3344  if (triple.q1 != EPMEM_NODEID_ROOT) {
3345  if (type == EPMEM_RIT_STATE_NODE) {
3346  std::cout << "\"n" << triple.q1 << "\" [shape=\"rect\"];" << std::endl;
3347  }
3348  std::cout << "\"e" << triple.q0 << "\" -> \"" << (type == EPMEM_RIT_STATE_NODE ? "n" : "e") << triple.q1 << "\" [label=\"" << triple.w << "\"];" << std::endl;
3349  }
3350  }
3351  }
3352  std::cout << "};" << std::endl;
3353  // PEDGES / LITERAL->PEDGE
3354  std::cout << "subgraph cluster_pedges {" << std::endl;
3355  std::cout << "node [fillcolor=\"#008000\"];" << std::endl;
3356  std::multimap<epmem_node_id, epmem_pedge*> parent_pedge_map;
3357  for (int type = EPMEM_RIT_STATE_NODE; type <= EPMEM_RIT_STATE_EDGE; type++) {
3358  for (epmem_triple_pedge_map::iterator pedge_iter = pedge_caches[type].begin(); pedge_iter != pedge_caches[type].end(); pedge_iter++) {
3359  epmem_triple triple = (*pedge_iter).first;
3360  epmem_pedge* pedge = (*pedge_iter).second;
3361  if (triple.w != EPMEM_NODEID_BAD) {
3362  std::cout << "\"" << pedge << "\" [label=\"" << pedge << "\\n(" << triple.q0 << ", " << triple.w << ", ";
3363  if (triple.q1 == EPMEM_NODEID_BAD) {
3364  std::cout << "?";
3365  } else {
3366  std::cout << triple.q1;
3367  }
3368  std::cout << ")\"";
3369  if (!pedge->value_is_id) {
3370  std::cout << ", shape=\"rect\"";
3371  }
3372  std::cout << "];" << std::endl;
3373  for (epmem_literal_set::iterator lit_iter = pedge->literals.begin(); lit_iter != pedge->literals.end(); lit_iter++) {
3374  epmem_literal* literal = *lit_iter;
3375  std::cout << "\"" << literal->value_sym << "\" -> \"" << pedge << "\";" << std::endl;
3376  }
3377  parent_pedge_map.insert(std::make_pair(triple.q0, pedge));
3378  }
3379  }
3380  }
3381  std::cout << "};" << std::endl;
3382  // PEDGE->PEDGE / PEDGE->NODE
3383  std::set<std::pair<epmem_pedge*, epmem_node_id> > drawn;
3384  for (int type = EPMEM_RIT_STATE_NODE; type <= EPMEM_RIT_STATE_EDGE; type++) {
3385  epmem_triple_uedge_map* uedge_cache = &uedge_caches[type];
3386  for (epmem_triple_uedge_map::iterator uedge_iter = uedge_cache->begin(); uedge_iter != uedge_cache->end(); uedge_iter++) {
3387  epmem_triple triple = (*uedge_iter).first;
3388  epmem_uedge* uedge = (*uedge_iter).second;
3389  if (triple.w != EPMEM_NODEID_BAD) {
3390  for (epmem_pedge_set::iterator pedge_iter = uedge->pedges.begin(); pedge_iter != uedge->pedges.end(); pedge_iter++) {
3391  epmem_pedge* pedge = *pedge_iter;
3392  std::pair<epmem_pedge*, epmem_node_id> pair = std::make_pair(pedge, triple.q0);
3393  if (!drawn.count(pair)) {
3394  drawn.insert(pair);
3395  std::cout << "\"" << pedge << "\" -> \"e" << triple.q0 << "\";" << std::endl;
3396  }
3397  std::cout << "\"" << pedge << "\" -> \"" << (pedge->value_is_id ? "e" : "n") << triple.q1 << "\" [style=\"dashed\"];" << std::endl;
3398  std::pair<std::multimap<epmem_node_id, epmem_pedge*>::iterator, std::multimap<epmem_node_id, epmem_pedge*>::iterator> pedge_iters = parent_pedge_map.equal_range(triple.q1);
3399  for (std::multimap<epmem_node_id, epmem_pedge*>::iterator pedge_iter = pedge_iters.first; pedge_iter != pedge_iters.second; pedge_iter++) {
3400  std::cout << "\"" << pedge << "\" -> \"" << (*pedge_iter).second << "\";" << std::endl;
3401  }
3402  }
3403  }
3404  }
3405  }
3406  std::cout << "}" << std::endl;
3407 }
3408 
3410  return (a->matches.size() < b->matches.size());
3411 }
3412 
3413 epmem_literal* epmem_build_dnf(wme* cue_wme, epmem_wme_literal_map& literal_cache, epmem_literal_set& leaf_literals, epmem_symbol_int_map& symbol_num_incoming, epmem_literal_deque& gm_ordering, epmem_symbol_set& currents, int query_type, std::set<Symbol*>& visiting, soar_module::wme_set& cue_wmes, agent* my_agent) {
3414  // if the value is being visited, this is part of a loop; return NULL
3415  // remove this check (and in fact, the entire visiting parameter) if cyclic cues are allowed
3416  if (visiting.count(cue_wme->value)) {
3417  return NULL;
3418  }
3419  // if the value is an identifier and we've been here before, we can return the previous literal
3420  if (literal_cache.count(cue_wme)) {
3421  return literal_cache[cue_wme];
3422  }
3423 
3424  cue_wmes.insert(cue_wme);
3425  Symbol* value = cue_wme->value;
3426  epmem_literal* literal;
3427  allocate_with_pool(my_agent, &(my_agent->epmem_literal_pool), &literal);
3428  new(&(literal->parents)) epmem_literal_set();
3429  new(&(literal->children)) epmem_literal_set();
3430 
3431  if (value->common.symbol_type != IDENTIFIER_SYMBOL_TYPE) { // WME is a value
3432  literal->value_is_id = EPMEM_RIT_STATE_NODE;
3433  literal->is_leaf = true;
3434  literal->q1 = epmem_temporal_hash(my_agent, value);
3435  leaf_literals.insert(literal);
3436  } else if (value->id.smem_lti) { // WME is an LTI
3437  // if we can find the LTI node id, cache it; otherwise, return failure
3438  my_agent->epmem_stmts_graph->find_lti->bind_int(1, static_cast<uint64_t>(value->id.name_letter));
3439  my_agent->epmem_stmts_graph->find_lti->bind_int(2, static_cast<uint64_t>(value->id.name_number));
3440  if (my_agent->epmem_stmts_graph->find_lti->execute() == soar_module::row) {
3441  literal->value_is_id = EPMEM_RIT_STATE_EDGE;
3442  literal->is_leaf = true;
3443  literal->q1 = my_agent->epmem_stmts_graph->find_lti->column_int(0);
3444  my_agent->epmem_stmts_graph->find_lti->reinitialize();
3445  leaf_literals.insert(literal);
3446  } else {
3447  my_agent->epmem_stmts_graph->find_lti->reinitialize();
3448  literal->parents.~epmem_literal_set();
3449  literal->children.~epmem_literal_set();
3450  free_with_pool(&(my_agent->epmem_literal_pool), literal);
3451  return NULL;
3452  }
3453  } else { // WME is a normal identifier
3454  // we determine whether it is a leaf by checking for children
3455  epmem_wme_list* children = epmem_get_augs_of_id(value, get_new_tc_number(my_agent));
3456  literal->value_is_id = EPMEM_RIT_STATE_EDGE;
3457  literal->q1 = EPMEM_NODEID_BAD;
3458 
3459  // if the WME has no children, then it's a leaf
3460  // otherwise, we recurse for all children
3461  if (children->empty()) {
3462  literal->is_leaf = true;
3463  leaf_literals.insert(literal);
3464  delete children;
3465  } else {
3466  bool cycle = false;
3467  visiting.insert(cue_wme->value);
3468  for (epmem_wme_list::iterator wme_iter = children->begin(); wme_iter != children->end(); wme_iter++) {
3469  // check to see if this child forms a cycle
3470  // if it does, we skip over it
3471  epmem_literal* child = epmem_build_dnf(*wme_iter, literal_cache, leaf_literals, symbol_num_incoming, gm_ordering, currents, query_type, visiting, cue_wmes, my_agent);
3472  if (child) {
3473  child->parents.insert(literal);
3474  literal->children.insert(child);
3475  } else {
3476  cycle = true;
3477  }
3478  }
3479  delete children;
3480  visiting.erase(cue_wme->value);
3481  // if all children of this WME lead to cycles, then we don't need to walk this path
3482  // in essence, this forces the DNF graph to be acyclic
3483  // this results in savings in not walking edges and intervals
3484  if (cycle && literal->children.empty()) {
3485  literal->parents.~epmem_literal_set();
3486  literal->children.~epmem_literal_set();
3487  free_with_pool(&(my_agent->epmem_literal_pool), literal);
3488  return NULL;
3489  }
3490  literal->is_leaf = false;
3491  epmem_symbol_int_map::iterator rem_iter = symbol_num_incoming.find(value);
3492  if (rem_iter == symbol_num_incoming.end()) {
3493  symbol_num_incoming[value] = 1;
3494  } else {
3495  (*rem_iter).second++;
3496  }
3497  }
3498  }
3499 
3500  if (!query_type) {
3501  gm_ordering.push_front(literal);
3502  }
3503 
3504  literal->id_sym = cue_wme->id;
3505  literal->value_sym = cue_wme->value;
3506  literal->is_current = (currents.count(value) > 0);
3507  literal->w = epmem_temporal_hash(my_agent, cue_wme->attr);
3508  literal->is_neg_q = query_type;
3509  literal->weight = (literal->is_neg_q ? -1 : 1) * (my_agent->epmem_params->balance->get_value() >= 1.0 - 1.0e-8 ? 1.0 : wma_get_wme_activation(my_agent, cue_wme, true));
3510 #ifdef USE_MEM_POOL_ALLOCATORS
3511  new(&(literal->matches)) epmem_node_pair_set(std::less<epmem_node_pair>(), soar_module::soar_memory_pool_allocator<epmem_node_pair>(my_agent));
3512 #else
3513  new(&(literal->matches)) epmem_node_pair_set();
3514 #endif
3515  new(&(literal->values)) epmem_node_int_map();
3516 
3517  literal_cache[cue_wme] = literal;
3518  return literal;
3519 }
3520 
3521 bool epmem_register_pedges(epmem_node_id parent, epmem_literal* literal, epmem_pedge_pq& pedge_pq, epmem_time_id after, epmem_triple_pedge_map pedge_caches[], epmem_triple_uedge_map uedge_caches[], agent* my_agent) {
3522  // we don't need to keep track of visited literals/nodes because the literals are guaranteed to be acyclic
3523  // that is, the expansion to the literal's children will eventually bottom out
3524  // select the query
3525  epmem_triple triple = {parent, literal->w, literal->q1};
3526  int is_edge = literal->value_is_id;
3527  if (QUERY_DEBUG >= 1) {
3528  std::cout << " RECURSING ON " << parent << " " << literal << std::endl;
3529  }
3530  // if the unique edge does not exist, create a new unique edge query
3531  // otherwse, if the pedge has not been registered with this literal
3532  epmem_triple_pedge_map* pedge_cache = &(pedge_caches[is_edge]);
3533  epmem_triple_pedge_map::iterator pedge_iter = pedge_cache->find(triple);
3534  epmem_pedge* child_pedge = (*pedge_iter).second;
3535  if (pedge_iter == pedge_cache->end() || (*pedge_iter).second == NULL) {
3536  int has_value = (literal->q1 != EPMEM_NODEID_BAD ? 1 : 0);
3538  int bind_pos = 1;
3539  if (!is_edge) {
3540  pedge_sql->bind_int(bind_pos++, LLONG_MAX);
3541  }
3542  pedge_sql->bind_int(bind_pos++, triple.q0);
3543  pedge_sql->bind_int(bind_pos++, triple.w);
3544  if (has_value) {
3545  pedge_sql->bind_int(bind_pos++, triple.q1);
3546  }
3547  if (is_edge) {
3548  pedge_sql->bind_int(bind_pos++, after);
3549  }
3550  if (pedge_sql->execute() == soar_module::row) {
3551  epmem_pedge* child_pedge;
3552  allocate_with_pool(my_agent, &(my_agent->epmem_pedge_pool), &child_pedge);
3553  child_pedge->triple = triple;
3554  child_pedge->value_is_id = literal->value_is_id;
3555  child_pedge->has_noncurrent = !literal->is_current;
3556  child_pedge->sql = pedge_sql;
3557  new(&(child_pedge->literals)) epmem_literal_set();
3558  child_pedge->literals.insert(literal);
3559  child_pedge->time = child_pedge->sql->column_int(2);
3560  pedge_pq.push(child_pedge);
3561  (*pedge_cache)[triple] = child_pedge;
3562  return true;
3563  } else {
3564  pedge_sql->get_pool()->release(pedge_sql);
3565  return false;
3566  }
3567  } else if (!child_pedge->literals.count(literal)) {
3568  child_pedge->literals.insert(literal);
3569  if (!literal->is_current) {
3570  child_pedge->has_noncurrent = true;
3571  }
3572  // if the literal is an edge with no specified value, add the literal to all potential pedges
3573  if (!literal->is_leaf && literal->q1 == EPMEM_NODEID_BAD) {
3574  bool created = false;
3575  epmem_triple_uedge_map* uedge_cache = &uedge_caches[is_edge];
3576  for (epmem_triple_uedge_map::iterator uedge_iter = uedge_cache->lower_bound(triple); uedge_iter != uedge_cache->end(); uedge_iter++) {
3577  epmem_triple child_triple = (*uedge_iter).first;
3578  // make sure we're still looking at the right edge(s)
3579  if (child_triple.q0 != triple.q0 || child_triple.w != triple.w) {
3580  break;
3581  }
3582  epmem_uedge* child_uedge = (*uedge_iter).second;
3583  if (child_triple.q1 != EPMEM_NODEID_BAD && child_uedge->value_is_id) {
3584  for (epmem_literal_set::iterator child_iter = literal->children.begin(); child_iter != literal->children.end(); child_iter++) {
3585  created |= epmem_register_pedges(child_triple.q1, *child_iter, pedge_pq, after, pedge_caches, uedge_caches, my_agent);
3586  }
3587  }
3588  }
3589  return created;
3590  }
3591  }
3592  return true;
3593 }
3594 
3595 bool epmem_satisfy_literal(epmem_literal* literal, epmem_node_id parent, epmem_node_id child, double& current_score, long int& current_cardinality, epmem_symbol_node_pair_int_map& symbol_node_count, epmem_triple_uedge_map uedge_caches[], epmem_symbol_int_map& symbol_num_incoming) {
3596  epmem_symbol_node_pair_int_map::iterator match_iter;
3597  if (QUERY_DEBUG >= 1) {
3598  std::cout << " RECURSING ON " << parent << " " << child << " " << literal << std::endl;
3599  }
3600  // check if the ancestors of this literal are satisfied
3601  bool parents_satisfied = (literal->id_sym == NULL);
3602  if (!parents_satisfied) {
3603  // ancestors are satisfied if:
3604  // 1. all incoming literals are satisfied
3605  // 2. all incoming literals have this particular node satisfying it
3606  int num_incoming = symbol_num_incoming[literal->id_sym];
3607  match_iter = symbol_node_count.find(std::make_pair(literal->id_sym, parent));
3608  // since, by definition, if a node satisfies all incoming literals, all incoming literals are satisfied
3609  parents_satisfied = (match_iter != symbol_node_count.end()) && ((*match_iter).second == num_incoming);
3610  }
3611  // if yes
3612  if (parents_satisfied) {
3613  // add the edge as a match
3614  literal->matches.insert(std::make_pair(parent, child));
3615  epmem_node_int_map::iterator values_iter = literal->values.find(child);
3616  if (values_iter == literal->values.end()) {
3617  literal->values[child] = 1;
3618  if (literal->is_leaf) {
3619  if (literal->matches.size() == 1) {
3620  current_score += literal->weight;
3621  current_cardinality += (literal->is_neg_q ? -1 : 1);
3622  if (QUERY_DEBUG >= 1) {
3623  std::cout << " NEW SCORE: " << current_score << ", " << current_cardinality << std::endl;
3624  }
3625  return true;
3626  }
3627  } else {
3628  bool changed_score = false;
3629  // change bookkeeping information about ancestry
3630  epmem_symbol_node_pair match = std::make_pair(literal->value_sym, child);
3631  match_iter = symbol_node_count.find(match);
3632  if (match_iter == symbol_node_count.end()) {
3633  symbol_node_count[match] = 1;
3634  } else {
3635  symbol_node_count[match]++;
3636  }
3637  // recurse over child literals
3638  for (epmem_literal_set::iterator child_iter = literal->children.begin(); child_iter != literal->children.end(); child_iter++) {
3639  epmem_literal* child_lit = *child_iter;
3640  epmem_triple_uedge_map* uedge_cache = &uedge_caches[child_lit->value_is_id];
3641  epmem_triple child_triple = {child, child_lit->w, child_lit->q1};
3642  epmem_triple_uedge_map::iterator uedge_iter;
3643  epmem_uedge* child_uedge = NULL;
3644  if (child_lit->q1 == EPMEM_NODEID_BAD) {
3645  uedge_iter = uedge_cache->lower_bound(child_triple);
3646  while (uedge_iter != uedge_cache->end()) {
3647  child_triple = (*uedge_iter).first;
3648  child_uedge = (*uedge_iter).second;
3649  if (child_triple.q0 != child || child_triple.w != child_lit->w) {
3650  break;
3651  }
3652  if (child_uedge->activated && (!literal->is_current || child_uedge->activation_count == 1)) {
3653  changed_score |= epmem_satisfy_literal(child_lit, child_triple.q0, child_triple.q1, current_score, current_cardinality, symbol_node_count, uedge_caches, symbol_num_incoming);
3654  }
3655  uedge_iter++;
3656  }
3657  } else {
3658  uedge_iter = uedge_cache->find(child_triple);
3659  child_uedge = (*uedge_iter).second;
3660  if (uedge_iter != uedge_cache->end() && child_uedge->activated && (!literal->is_current || child_uedge->activation_count == 1)) {
3661  changed_score |= epmem_satisfy_literal(child_lit, child_triple.q0, child_triple.q1, current_score, current_cardinality, symbol_node_count, uedge_caches, symbol_num_incoming);
3662  }
3663  }
3664  }
3665  return changed_score;
3666  }
3667  } else {
3668  (*values_iter).second++;
3669  }
3670  }
3671  return false;
3672 }
3673 
3674 bool epmem_unsatisfy_literal(epmem_literal* literal, epmem_node_id parent, epmem_node_id child, double& current_score, long int& current_cardinality, epmem_symbol_node_pair_int_map& symbol_node_count) {
3675  epmem_symbol_int_map::iterator count_iter;
3676  if (literal->matches.size() == 0) {
3677  return false;
3678  }
3679  if (QUERY_DEBUG >= 1) {
3680  std::cout << " RECURSING ON " << parent << " " << child << " " << literal << std::endl;
3681  }
3682  // we only need things if this parent-child pair is matching the literal
3683  epmem_node_pair_set::iterator lit_match_iter = literal->matches.find(std::make_pair(parent, child));
3684  if (lit_match_iter != literal->matches.end()) {
3685  // erase the edge from this literal's matches
3686  literal->matches.erase(lit_match_iter);
3687  epmem_node_int_map::iterator values_iter = literal->values.find(child);
3688  (*values_iter).second--;
3689  if ((*values_iter).second == 0) {
3690  literal->values.erase(values_iter);
3691  if (literal->is_leaf) {
3692  if (literal->matches.size() == 0) {
3693  current_score -= literal->weight;
3694  current_cardinality -= (literal->is_neg_q ? -1 : 1);
3695  if (QUERY_DEBUG >= 1) {
3696  std::cout << " NEW SCORE: " << current_score << ", " << current_cardinality << std::endl;
3697  }
3698  return true;
3699  }
3700  } else {
3701  bool changed_score = false;
3702  epmem_symbol_node_pair match = std::make_pair(literal->value_sym, child);
3703  epmem_symbol_node_pair_int_map::iterator match_iter = symbol_node_count.find(match);
3704  (*match_iter).second--;
3705  if ((*match_iter).second == 0) {
3706  symbol_node_count.erase(match_iter);
3707  }
3708  // if this literal is no longer satisfied, recurse on all children
3709  // if this literal is still satisfied, recurse on children who is matching on descendants of this edge
3710  if (literal->matches.size() == 0) {
3711  for (epmem_literal_set::iterator child_iter = literal->children.begin(); child_iter != literal->children.end(); child_iter++) {
3712  epmem_literal* child_lit = *child_iter;
3713  for (epmem_node_pair_set::iterator node_iter = child_lit->matches.begin(); node_iter != child_lit->matches.end(); node_iter++) {
3714  changed_score |= epmem_unsatisfy_literal(child_lit, (*node_iter).first, (*node_iter).second, current_score, current_cardinality, symbol_node_count);
3715  }
3716  }
3717  } else {
3718  epmem_node_pair node_pair = std::make_pair(child, EPMEM_NODEID_BAD);
3719  for (epmem_literal_set::iterator child_iter = literal->children.begin(); child_iter != literal->children.end(); child_iter++) {
3720  epmem_literal* child_lit = *child_iter;
3721  epmem_node_pair_set::iterator node_iter = child_lit->matches.lower_bound(node_pair);
3722  if (node_iter != child_lit->matches.end() && (*node_iter).first == child) {
3723  changed_score |= epmem_unsatisfy_literal(child_lit, (*node_iter).first, (*node_iter).second, current_score, current_cardinality, symbol_node_count);
3724  }
3725  }
3726  }
3727  return changed_score;
3728  }
3729  }
3730  }
3731  return false;
3732 }
3733 
3734 bool epmem_graph_match(epmem_literal_deque::iterator& dnf_iter, epmem_literal_deque::iterator& iter_end, epmem_literal_node_pair_map& bindings, epmem_node_symbol_map bound_nodes[], agent* my_agent, int depth = 0) {
3735  if (dnf_iter == iter_end) {
3736  return true;
3737  }
3738  epmem_literal* literal = *dnf_iter;
3739  if (bindings.count(literal)) {
3740  return false;
3741  }
3742  epmem_literal_deque::iterator next_iter = dnf_iter;
3743  next_iter++;
3744 #ifdef USE_MEM_POOL_ALLOCATORS
3745  epmem_node_set failed_parents = epmem_node_set(std::less<epmem_node_id>(), soar_module::soar_memory_pool_allocator<epmem_node_id>(my_agent));
3746  epmem_node_set failed_children = epmem_node_set(std::less<epmem_node_id>(), soar_module::soar_memory_pool_allocator<epmem_node_id>(my_agent));
3747 #else
3748  epmem_node_set failed_parents;
3749  epmem_node_set failed_children;
3750 #endif
3751  // go through the list of matches, binding each one to this literal in turn
3752  for (epmem_node_pair_set::iterator match_iter = literal->matches.begin(); match_iter != literal->matches.end(); match_iter++) {
3753  epmem_node_id q0 = (*match_iter).first;
3754  epmem_node_id q1 = (*match_iter).second;
3755  if (failed_parents.count(q0)) {
3756  continue;
3757  }
3758  if (QUERY_DEBUG >= 2) {
3759  for (int i = 0; i < depth; i++) {
3760  std::cout << "\t";
3761  }
3762  std::cout << "TRYING " << literal << " " << q0 << std::endl;
3763  }
3764  bool relations_okay = true;
3765  // for all parents
3766  for (epmem_literal_set::iterator parent_iter = literal->parents.begin(); relations_okay && parent_iter != literal->parents.end(); parent_iter++) {
3767  epmem_literal* parent = *parent_iter;
3768  epmem_literal_node_pair_map::iterator bind_iter = bindings.find(parent);
3769  if (bind_iter != bindings.end() && (*bind_iter).second.second != q0) {
3770  relations_okay = false;
3771  }
3772  }
3773  if (!relations_okay) {
3774  if (QUERY_DEBUG >= 2) {
3775  for (int i = 0; i < depth; i++) {
3776  std::cout << "\t";
3777  }
3778  std::cout << "PARENT CONSTRAINT FAIL" << std::endl;
3779  }
3780  failed_parents.insert(q0);
3781  continue;
3782  }
3783  // if the node has already been bound, make sure it's bound to the same thing
3784  epmem_node_symbol_map::iterator binder = bound_nodes[literal->value_is_id].find(q1);
3785  if (binder != bound_nodes[literal->value_is_id].end() && (*binder).second != literal->value_sym) {
3786  failed_children.insert(q1);
3787  continue;
3788  }
3789  if (QUERY_DEBUG >= 2) {
3790  for (int i = 0; i < depth; i++) {
3791  std::cout << "\t";
3792  }
3793  std::cout << "TRYING " << literal << " " << q0 << " " << q1 << std::endl;
3794  }
3795  if (literal->q1 != EPMEM_NODEID_BAD && literal->q1 != q1) {
3796  relations_okay = false;
3797  }
3798  // for all children
3799  for (epmem_literal_set::iterator child_iter = literal->children.begin(); relations_okay && child_iter != literal->children.end(); child_iter++) {
3800  epmem_literal* child = *child_iter;
3801  epmem_literal_node_pair_map::iterator bind_iter = bindings.find(child);
3802  if (bind_iter != bindings.end() && (*bind_iter).second.first != q1) {
3803  relations_okay = false;
3804  }
3805  }
3806  if (!relations_okay) {
3807  if (QUERY_DEBUG >= 2) {
3808  for (int i = 0; i < depth; i++) {
3809  std::cout << "\t";
3810  }
3811  std::cout << "CHILD CONSTRAINT FAIL" << std::endl;
3812  }
3813  failed_children.insert(q1);
3814  continue;
3815  }
3816  if (QUERY_DEBUG >= 2) {
3817  for (int i = 0; i < depth; i++) {
3818  std::cout << "\t";
3819  }
3820  std::cout << literal << " " << q0 << " " << q1 << std::endl;
3821  }
3822  // temporarily modify the bindings and bound nodes
3823  bindings[literal] = std::make_pair(q0, q1);
3824  bound_nodes[literal->value_is_id][q1] = literal->value_sym;
3825  // recurse on the rest of the list
3826  bool list_satisfied = epmem_graph_match(next_iter, iter_end, bindings, bound_nodes, my_agent, depth + 1);
3827  // if the rest of the list matched, we've succeeded
3828  // otherwise, undo the temporarily modifications and try again
3829  if (list_satisfied) {
3830  return true;
3831  } else {
3832  bindings.erase(literal);
3833  bound_nodes[literal->value_is_id].erase(q1);
3834  }
3835  }
3836  // this means we've tried everything and this whole exercise was a waste of time
3837  // EPIC FAIL
3838  if (QUERY_DEBUG >= 2) {
3839  for (int i = 0; i < depth; i++) {
3840  std::cout << "\t";
3841  }
3842  std::cout << "EPIC FAIL" << std::endl;
3843  }
3844  return false;
3845 }
3846 
3847 void epmem_process_query(agent *my_agent, Symbol *state, Symbol *pos_query, Symbol *neg_query, epmem_time_list& prohibits, epmem_time_id before, epmem_time_id after, epmem_symbol_set& currents, soar_module::wme_set& cue_wmes, soar_module::symbol_triple_list& meta_wmes, soar_module::symbol_triple_list& retrieval_wmes, int level=3) {
3848  // a query must contain a positive cue
3849  if (pos_query == NULL) {
3850  epmem_buffer_add_wme(meta_wmes, state->id.epmem_result_header, my_agent->epmem_sym_status, my_agent->epmem_sym_bad_cmd);
3851  return;
3852  }
3853 
3854  // before and after, if specified, must be valid relative to each other
3855  if (before != EPMEM_MEMID_NONE && after != EPMEM_MEMID_NONE && before <= after) {
3856  epmem_buffer_add_wme(meta_wmes, state->id.epmem_result_header, my_agent->epmem_sym_status, my_agent->epmem_sym_bad_cmd);
3857  return;
3858  }
3859 
3860  if (QUERY_DEBUG >= 1) {
3861  std::cout << std::endl << "==========================" << std::endl << std::endl;
3862  }
3863 
3864  my_agent->epmem_timers->query->start();
3865 
3866  // sort probibit's
3867  if (!prohibits.empty()) {
3868  std::sort(prohibits.begin(), prohibits.end());
3869  }
3870 
3871  // epmem options
3872  bool do_graph_match = (my_agent->epmem_params->graph_match->get_value() == soar_module::on);
3874 
3875  // variables needed for cleanup
3876  epmem_wme_literal_map literal_cache;
3877  epmem_triple_pedge_map pedge_caches[2];
3878 #ifdef USE_MEM_POOL_ALLOCATORS
3879  epmem_triple_uedge_map uedge_caches[2] = {
3880  epmem_triple_uedge_map(std::less<epmem_triple>(), soar_module::soar_memory_pool_allocator<std::pair<const epmem_triple, epmem_uedge*> >(my_agent)),
3881  epmem_triple_uedge_map(std::less<epmem_triple>(), soar_module::soar_memory_pool_allocator<std::pair<const epmem_triple, epmem_uedge*> >(my_agent))
3882  };
3883  epmem_interval_set interval_cleanup = epmem_interval_set(std::less<epmem_interval*>(), soar_module::soar_memory_pool_allocator<epmem_interval*>(my_agent));
3884 #else
3886  epmem_interval_set interval_cleanup = epmem_interval_set();
3887 #endif
3888 
3889  // TODO additional indices
3890 
3891  // variables needed for building the DNF
3892  epmem_literal* root_literal;
3893  allocate_with_pool(my_agent, &(my_agent->epmem_literal_pool), &root_literal);
3894  epmem_literal_set leaf_literals;
3895 
3896  // priority queues for interval walk
3897  epmem_pedge_pq pedge_pq;
3898  epmem_interval_pq interval_pq;
3899 
3900  // variables needed to track satisfiability
3901  epmem_symbol_int_map symbol_num_incoming; // number of literals with a certain symbol as its value
3902  epmem_symbol_node_pair_int_map symbol_node_count; // number of times a symbol is matched by a node
3903 
3904  // various things about the current and the best episodes
3905  epmem_time_id best_episode = EPMEM_MEMID_NONE;
3906  double best_score = 0;
3907  bool best_graph_matched = false;
3908  long int best_cardinality = 0;
3909  epmem_literal_node_pair_map best_bindings;
3910  double current_score = 0;
3911  long int current_cardinality = 0;
3912 
3913  // variables needed for graphmatch
3914  epmem_literal_deque gm_ordering;
3915 
3916  if (level > 1) {
3917  // build the DNF graph while checking for leaf WMEs
3918  {
3919  my_agent->epmem_timers->query_dnf->start();
3920  root_literal->id_sym = NULL;
3921  root_literal->value_sym = pos_query;
3922  root_literal->is_neg_q = EPMEM_NODE_POS;
3923  root_literal->value_is_id = EPMEM_RIT_STATE_EDGE;
3924  root_literal->is_leaf = false;
3925  root_literal->is_current = false;
3926  root_literal->w = EPMEM_NODEID_BAD;
3927  root_literal->q1 = EPMEM_NODEID_ROOT;
3928  root_literal->weight = 0.0;
3929  new(&(root_literal->parents)) epmem_literal_set();
3930  new(&(root_literal->children)) epmem_literal_set();
3931 #ifdef USE_MEM_POOL_ALLOCATORS
3932  new(&(root_literal->matches)) epmem_node_pair_set(std::less<epmem_node_pair>(), soar_module::soar_memory_pool_allocator<epmem_node_pair>(my_agent));
3933 #else
3934  new(&(root_literal->matches)) epmem_node_pair_set();
3935 #endif
3936  new(&(root_literal->values)) epmem_node_int_map();
3937  symbol_num_incoming[pos_query] = 1;
3938  literal_cache[NULL] = root_literal;
3939 
3940  std::set<Symbol*> visiting;
3941  visiting.insert(pos_query);
3942  visiting.insert(neg_query);
3943  for (int query_type = EPMEM_NODE_POS; query_type <= EPMEM_NODE_NEG; query_type++) {
3944  Symbol* query_root = NULL;
3945  switch (query_type) {
3946  case EPMEM_NODE_POS:
3947  query_root = pos_query;
3948  break;
3949  case EPMEM_NODE_NEG:
3950  query_root = neg_query;
3951  break;
3952  }
3953  if (!query_root) {
3954  continue;
3955  }
3956  epmem_wme_list* children = epmem_get_augs_of_id(query_root, get_new_tc_number(my_agent));
3957  // for each first level WME, build up a DNF
3958  for (epmem_wme_list::iterator wme_iter = children->begin(); wme_iter != children->end(); wme_iter++) {
3959  epmem_literal* child = epmem_build_dnf(*wme_iter, literal_cache, leaf_literals, symbol_num_incoming, gm_ordering, currents, query_type, visiting, cue_wmes, my_agent);
3960  if (child) {
3961  // force all first level literals to have the same id symbol
3962  child->id_sym = pos_query;
3963  child->parents.insert(root_literal);
3964  root_literal->children.insert(child);
3965  }
3966  }
3967  delete children;
3968  }
3969  my_agent->epmem_timers->query_dnf->stop();
3970  }
3971 
3972  // calculate the highest possible score and cardinality score
3973  double perfect_score = 0;
3974  int perfect_cardinality = 0;
3975  for (epmem_literal_set::iterator iter = leaf_literals.begin(); iter != leaf_literals.end(); iter++) {
3976  if (!(*iter)->is_neg_q) {
3977  perfect_score += (*iter)->weight;
3978  perfect_cardinality++;
3979  }
3980  }
3981 
3982  // set default values for before and after
3983  if (before == EPMEM_MEMID_NONE) {
3984  before = my_agent->epmem_stats->time->get_value() - 1;
3985  } else {
3986  before = before - 1; // since before's are strict
3987  }
3988  if (after == EPMEM_MEMID_NONE) {
3989  after = EPMEM_MEMID_NONE;
3990  }
3991  epmem_time_id current_episode = before;
3992  epmem_time_id next_episode;
3993 
3994  // create dummy edges and intervals
3995  {
3996  // insert dummy unique edge and interval end point queries for DNF root
3997  // we make an SQL statement just so we don't have to do anything special at cleanup
3999  epmem_pedge* root_pedge;
4000  allocate_with_pool(my_agent, &(my_agent->epmem_pedge_pool), &root_pedge);
4001  root_pedge->triple = triple;
4002  root_pedge->value_is_id = EPMEM_RIT_STATE_EDGE;
4003  root_pedge->has_noncurrent = false;
4004  new(&(root_pedge->literals)) epmem_literal_set();
4005  root_pedge->literals.insert(root_literal);
4006  root_pedge->sql = my_agent->epmem_stmts_graph->pool_dummy->request();
4007  root_pedge->sql->prepare();
4008  root_pedge->sql->bind_int(1, LLONG_MAX);
4009  root_pedge->sql->execute( soar_module::op_reinit );
4010  root_pedge->time = LLONG_MAX;
4011  pedge_pq.push(root_pedge);
4012  pedge_caches[EPMEM_RIT_STATE_EDGE][triple] = root_pedge;
4013 
4014  epmem_uedge* root_uedge;
4015  allocate_with_pool(my_agent, &(my_agent->epmem_uedge_pool), &root_uedge);
4016  root_uedge->triple = triple;
4017  root_uedge->value_is_id = EPMEM_RIT_STATE_EDGE;
4018  root_uedge->has_noncurrent = false;
4019  root_uedge->activation_count = 0;
4020  new(&(root_uedge->pedges)) epmem_pedge_set();
4021  root_uedge->intervals = 1;
4022  root_uedge->activated = false;
4023  uedge_caches[EPMEM_RIT_STATE_EDGE][triple] = root_uedge;
4024 
4025  epmem_interval* root_interval;
4026  allocate_with_pool(my_agent, &(my_agent->epmem_interval_pool), &root_interval);
4027  root_interval->uedge = root_uedge;
4028  root_interval->is_end_point = true;
4029  root_interval->sql = my_agent->epmem_stmts_graph->pool_dummy->request();
4030  root_interval->sql->prepare();
4031  root_interval->sql->bind_int(1, before);
4032  root_interval->sql->execute( soar_module::op_reinit );
4033  root_interval->time = before;
4034  interval_pq.push(root_interval);
4035  interval_cleanup.insert(root_interval);
4036  }
4037 
4038  if (QUERY_DEBUG >= 1) {
4039  epmem_print_retrieval_state(literal_cache, pedge_caches, uedge_caches);
4040  }
4041 
4042 #ifdef EPMEM_EXPERIMENT
4043  epmem_episodes_searched = 0;
4044 #endif
4045 
4046  // main loop of interval walk
4047  my_agent->epmem_timers->query_walk->start();
4048  while (pedge_pq.size() && current_episode > after) {
4049  epmem_time_id next_edge;
4050  epmem_time_id next_interval;
4051 
4052  bool changed_score = false;
4053 
4054  my_agent->epmem_timers->query_walk_edge->start();
4055  next_edge = pedge_pq.top()->time;
4056 
4057  // process all edges which were last used at this time point
4058  while (pedge_pq.size() && (pedge_pq.top()->time == next_edge || pedge_pq.top()->time >= current_episode)) {
4059  epmem_pedge* pedge = pedge_pq.top();
4060  pedge_pq.pop();
4061  epmem_triple triple = pedge->triple;
4062  triple.q1 = pedge->sql->column_int(1);
4063 
4064  if (QUERY_DEBUG >= 1) {
4065  std::cout << " EDGE " << triple.q0 << "-" << triple.w << "-" << triple.q1 << std::endl;
4066  }
4067 
4068  // create queries for the unique edge children of this partial edge
4069  if (pedge->value_is_id) {
4070  bool created = false;
4071  for (epmem_literal_set::iterator literal_iter = pedge->literals.begin(); literal_iter != pedge->literals.end(); literal_iter++) {
4072  epmem_literal* literal = *literal_iter;
4073  for (epmem_literal_set::iterator child_iter = literal->children.begin(); child_iter != literal->children.end(); child_iter++) {
4074  created |= epmem_register_pedges(triple.q1, *child_iter, pedge_pq, after, pedge_caches, uedge_caches, my_agent);
4075  }
4076  }
4077  }
4078  // TODO what I want to do here is, if there is no children which leads to a leaf, retract everything
4079  // I'm not sure how to properly test for this though
4080 
4081  // look for uedge with triple; if none exist, create one
4082  // otherwise, link up the uedge with the pedge and consider score changes
4083  epmem_triple_uedge_map* uedge_cache = &uedge_caches[pedge->value_is_id];
4084  epmem_triple_uedge_map::iterator uedge_iter = uedge_cache->find(triple);
4085  if (uedge_iter == uedge_cache->end()) {
4086  // create a uedge for this
4087  epmem_uedge* uedge;
4088  allocate_with_pool(my_agent, &(my_agent->epmem_uedge_pool), &uedge);
4089  uedge->triple = triple;
4090  uedge->value_is_id = pedge->value_is_id;
4091  uedge->has_noncurrent = pedge->has_noncurrent;
4092  uedge->activation_count = 0;
4093  new(&(uedge->pedges)) epmem_pedge_set();
4094  uedge->intervals = 0;
4095  uedge->activated = false;
4096  // create interval queries for this partial edge
4097  bool created = false;
4098  int64_t edge_id = pedge->sql->column_int(0);
4099  epmem_time_id promo_time = EPMEM_MEMID_NONE;
4100  bool is_lti = (pedge->value_is_id && pedge->triple.q1 != EPMEM_NODEID_BAD && pedge->triple.q1 != EPMEM_NODEID_ROOT);
4101  if (is_lti) {
4102  // find the promotion time of the LTI
4103  my_agent->epmem_stmts_graph->find_lti_promotion_time->bind_int(1, triple.q1);
4105  promo_time = my_agent->epmem_stmts_graph->find_lti_promotion_time->column_int(0);
4107  }
4108  for (int interval_type = EPMEM_RANGE_EP; interval_type <= EPMEM_RANGE_POINT; interval_type++) {
4109  for (int point_type = EPMEM_RANGE_START; point_type <= EPMEM_RANGE_END; point_type++) {
4110  // pick a timer (any timer)
4111  soar_module::timer* sql_timer = NULL;
4112  switch (interval_type) {
4113  case EPMEM_RANGE_EP:
4114  if (point_type == EPMEM_RANGE_START) {
4115  sql_timer = my_agent->epmem_timers->query_sql_start_ep;
4116  } else {
4117  sql_timer = my_agent->epmem_timers->query_sql_end_ep;
4118  }
4119  break;
4120  case EPMEM_RANGE_NOW:
4121  if (point_type == EPMEM_RANGE_START) {
4122  sql_timer = my_agent->epmem_timers->query_sql_start_now;
4123  } else {
4124  sql_timer = my_agent->epmem_timers->query_sql_end_now;
4125  }
4126  break;
4127  case EPMEM_RANGE_POINT:
4128  if (point_type == EPMEM_RANGE_START) {
4129  sql_timer = my_agent->epmem_timers->query_sql_start_point;
4130  } else {
4131  sql_timer = my_agent->epmem_timers->query_sql_end_point;
4132  }
4133  break;
4134  }
4135  // create the SQL query and bind it
4136  // try to find an existing query first; if none exist, allocate a new one from the memory pools
4137  soar_module::pooled_sqlite_statement* interval_sql = NULL;
4138  if (is_lti) {
4139  interval_sql = my_agent->epmem_stmts_graph->pool_find_lti_queries[point_type][interval_type]->request(sql_timer);
4140  } else {
4141  interval_sql = my_agent->epmem_stmts_graph->pool_find_interval_queries[pedge->value_is_id][point_type][interval_type]->request(sql_timer);
4142  }
4143  int bind_pos = 1;
4144  if (point_type == EPMEM_RANGE_END && interval_type == EPMEM_RANGE_NOW) {
4145  interval_sql->bind_int(bind_pos++, current_episode);
4146  }
4147  interval_sql->bind_int(bind_pos++, edge_id);
4148  if (is_lti) {
4149  // find the promotion time of the LTI, and use that as an after constraint
4150  interval_sql->bind_int(bind_pos++, promo_time);
4151  }
4152  interval_sql->bind_int(bind_pos++, current_episode);
4153  if (interval_sql->execute() == soar_module::row) {
4154  epmem_interval* interval;
4155  allocate_with_pool(my_agent, &(my_agent->epmem_interval_pool), &interval);
4156  interval->is_end_point = point_type;
4157  interval->uedge = uedge;
4158  interval->time = interval_sql->column_int(0);
4159  interval->sql = interval_sql;
4160  interval_pq.push(interval);
4161  interval_cleanup.insert(interval);
4162  uedge->intervals++;
4163  created = true;
4164  } else {
4165  interval_sql->get_pool()->release(interval_sql);
4166  }
4167  }
4168  }
4169  if (created) {
4170  if (is_lti) {
4171  // insert a dummy promo time start for LTIs
4172  epmem_interval* start_interval;
4173  allocate_with_pool(my_agent, &(my_agent->epmem_interval_pool), &start_interval);
4174  start_interval->uedge = uedge;
4175  start_interval->is_end_point = EPMEM_RANGE_START;
4176  start_interval->time = promo_time - 1;
4177  start_interval->sql = NULL;
4178  interval_pq.push(start_interval);
4179  interval_cleanup.insert(start_interval);
4180  }
4181  uedge->pedges.insert(pedge);
4182  uedge_cache->insert(std::make_pair(triple, uedge));
4183  } else {
4184  uedge->pedges.~epmem_pedge_set();
4185  free_with_pool(&(my_agent->epmem_uedge_pool), uedge);
4186  }
4187  } else {
4188  epmem_uedge* uedge = (*uedge_iter).second;
4189  uedge->pedges.insert(pedge);
4190  if (uedge->activated) {
4191  for (epmem_literal_set::iterator lit_iter = pedge->literals.begin(); lit_iter != pedge->literals.end(); lit_iter++) {
4192  epmem_literal* literal = (*lit_iter);
4193  if (!literal->is_current || uedge->activation_count == 1) {
4194  changed_score |= epmem_satisfy_literal(literal, triple.q0, triple.q1, current_score, current_cardinality, symbol_node_count, uedge_caches, symbol_num_incoming);
4195  }
4196  }
4197  }
4198  }
4199 
4200  // put the partial edge query back into the queue if there's more
4201  // otherwise, reinitialize the query and put it in a pool
4202  if (pedge->sql && pedge->sql->execute() == soar_module::row) {
4203  pedge->time = pedge->sql->column_int(2);
4204  pedge_pq.push(pedge);
4205  } else if (pedge->sql) {
4206  pedge->sql->get_pool()->release(pedge->sql);
4207  pedge->sql = NULL;
4208  }
4209  }
4210  next_edge = (pedge_pq.empty() ? after : pedge_pq.top()->time);
4211  my_agent->epmem_timers->query_walk_edge->stop();
4212 
4213  // process all intervals before the next edge arrives
4214  my_agent->epmem_timers->query_walk_interval->start();
4215  while (interval_pq.size() && interval_pq.top()->time > next_edge && current_episode > after) {
4216  if (QUERY_DEBUG >= 1) {
4217  std::cout << "EPISODE " << current_episode << std::endl;
4218  }
4219  // process all interval endpoints at this time step
4220  while (interval_pq.size() && interval_pq.top()->time >= current_episode) {
4221  epmem_interval* interval = interval_pq.top();
4222  interval_pq.pop();
4223  epmem_uedge* uedge = interval->uedge;
4224  epmem_triple triple = uedge->triple;
4225  if (QUERY_DEBUG >= 1) {
4226  std::cout << " INTERVAL (" << (interval->is_end_point ? "end" : "start") << "): " << triple.q0 << "-" << triple.w << "-" << triple.q1 << std::endl;
4227  }
4228  if (interval->is_end_point) {
4229  uedge->activated = true;
4230  uedge->activation_count++;
4231  for (epmem_pedge_set::iterator pedge_iter = uedge->pedges.begin(); pedge_iter != uedge->pedges.end(); pedge_iter++) {
4232  epmem_pedge* pedge = *pedge_iter;
4233  for (epmem_literal_set::iterator lit_iter = pedge->literals.begin(); lit_iter != pedge->literals.end(); lit_iter++) {
4234  epmem_literal* literal = *lit_iter;
4235  if (!literal->is_current || uedge->activation_count == 1) {
4236  changed_score |= epmem_satisfy_literal(literal, triple.q0, triple.q1, current_score, current_cardinality, symbol_node_count, uedge_caches, symbol_num_incoming);
4237  }
4238  }
4239  }
4240  } else {
4241  uedge->activated = false;
4242  for (epmem_pedge_set::iterator pedge_iter = uedge->pedges.begin(); pedge_iter != uedge->pedges.end(); pedge_iter++) {
4243  epmem_pedge* pedge = *pedge_iter;
4244  for (epmem_literal_set::iterator lit_iter = pedge->literals.begin(); lit_iter != pedge->literals.end(); lit_iter++) {
4245  changed_score |= epmem_unsatisfy_literal(*lit_iter, triple.q0, triple.q1, current_score, current_cardinality, symbol_node_count);
4246  }
4247  }
4248  }
4249  // put the interval query back into the queue if there's more and some literal cares
4250  // otherwise, reinitialize the query and put it in a pool
4251  if (interval->uedge->has_noncurrent && interval->sql && interval->sql->execute() == soar_module::row) {
4252  interval->time = interval->sql->column_int(0);
4253  interval_pq.push(interval);
4254  } else if (interval->sql) {
4255  interval->sql->get_pool()->release(interval->sql);
4256  interval->sql = NULL;
4257  uedge->intervals--;
4258  if (uedge->intervals) {
4259  interval_cleanup.erase(interval);
4260  free_with_pool(&(my_agent->epmem_interval_pool), interval);
4261  } else {
4262  // TODO retract intervals
4263  }
4264  }
4265  }
4266  next_interval = (interval_pq.empty() ? after : interval_pq.top()->time);
4267  next_episode = (next_edge > next_interval ? next_edge : next_interval);
4268 
4269  // update the prohibits list to catch up
4270  while (prohibits.size() && prohibits.back() > current_episode) {
4271  prohibits.pop_back();
4272  }
4273  // ignore the episode if it is prohibited
4274  while (prohibits.size() && current_episode > next_episode && current_episode == prohibits.back()) {
4275  current_episode--;
4276  prohibits.pop_back();
4277  }
4278 
4279  if (QUERY_DEBUG >= 2) {
4280  epmem_print_retrieval_state(literal_cache, pedge_caches, uedge_caches);
4281  }
4282 
4283  if (my_agent->sysparams[TRACE_EPMEM_SYSPARAM]) {
4284  char buf[256];
4285  SNPRINTF(buf, 254, "CONSIDERING EPISODE (time, cardinality, score) (%lld, %ld, %f)\n", static_cast<long long int>(current_episode), current_cardinality, current_score);
4286  print(my_agent, buf);
4287  xml_generate_warning(my_agent, buf);
4288  }
4289 
4290 #ifdef EPMEM_EXPERIMENT
4291  epmem_episodes_searched++;
4292 #endif
4293 
4294  // if
4295  // * the current time is still before any new intervals
4296  // * and the score was changed in this period
4297  // * and the new score is higher than the best score
4298  // then save the current time as the best one
4299  if (current_episode > next_episode && changed_score && (best_episode == EPMEM_MEMID_NONE || current_score > best_score || (do_graph_match && current_score == best_score && !best_graph_matched))) {
4300  bool new_king = false;
4301  if (best_episode == EPMEM_MEMID_NONE || current_score > best_score) {
4302  best_episode = current_episode;
4303  best_score = current_score;
4304  best_cardinality = current_cardinality;
4305  new_king = true;
4306  }
4307  // we should graph match if the option is set and all leaf literals are satisfied
4308  if (current_cardinality == perfect_cardinality) {
4309  bool graph_matched = false;
4310  if (do_graph_match) {
4312  std::sort(gm_ordering.begin(), gm_ordering.end());
4313  } else if (gm_order == epmem_param_container::gm_order_mcv) {
4314  std::sort(gm_ordering.begin(), gm_ordering.end(), epmem_gm_mcv_comparator);
4315  }
4316  epmem_literal_deque::iterator begin = gm_ordering.begin();
4317  epmem_literal_deque::iterator end = gm_ordering.end();
4318  best_bindings.clear();
4319  epmem_node_symbol_map bound_nodes[2];
4320  if (QUERY_DEBUG >= 1) {
4321  std::cout << " GRAPH MATCH" << std::endl;
4322  epmem_print_retrieval_state(literal_cache, pedge_caches, uedge_caches);
4323  }
4324  my_agent->epmem_timers->query_graph_match->start();
4325  graph_matched = epmem_graph_match(begin, end, best_bindings, bound_nodes, my_agent, 2);
4326  my_agent->epmem_timers->query_graph_match->stop();
4327  }
4328  if (!do_graph_match || graph_matched) {
4329  best_episode = current_episode;
4330  best_graph_matched = true;
4331  current_episode = EPMEM_MEMID_NONE;
4332  new_king = true;
4333  }
4334  }
4335  if (new_king && my_agent->sysparams[TRACE_EPMEM_SYSPARAM]) {
4336  char buf[256];
4337  SNPRINTF(buf, 254, "NEW KING (perfect, graph-match): (%s, %s)\n", (current_cardinality == perfect_cardinality ? "true" : "false"), (best_graph_matched ? "true" : "false"));
4338  print(my_agent, buf);
4339  xml_generate_warning(my_agent, buf);
4340  }
4341  }
4342 
4343  if (current_episode == EPMEM_MEMID_NONE) {
4344  break;
4345  } else {
4346  current_episode = next_episode;
4347  }
4348  }
4349  my_agent->epmem_timers->query_walk_interval->stop();
4350  }
4351  my_agent->epmem_timers->query_walk->stop();
4352 
4353  // if the best episode is the default, fail
4354  // otherwise, put the episode in working memory
4355  if (best_episode == EPMEM_MEMID_NONE) {
4356  epmem_buffer_add_wme(meta_wmes, state->id.epmem_result_header, my_agent->epmem_sym_failure, pos_query);
4357  if (neg_query) {
4358  epmem_buffer_add_wme(meta_wmes, state->id.epmem_result_header, my_agent->epmem_sym_failure, neg_query);
4359  }
4360  } else {
4361  my_agent->epmem_timers->query_result->start();
4362  Symbol* temp_sym;
4363  epmem_id_mapping node_map_map;
4364  epmem_id_mapping node_mem_map;
4365  // cue size
4366  temp_sym = make_int_constant(my_agent, leaf_literals.size());
4367  epmem_buffer_add_wme(meta_wmes, state->id.epmem_result_header, my_agent->epmem_sym_cue_size, temp_sym);
4368  symbol_remove_ref(my_agent, temp_sym);
4369  // match cardinality
4370  temp_sym = make_int_constant(my_agent, best_cardinality);
4371  epmem_buffer_add_wme(meta_wmes, state->id.epmem_result_header, my_agent->epmem_sym_match_cardinality, temp_sym);
4372  symbol_remove_ref(my_agent, temp_sym);
4373  // match score
4374  temp_sym = make_float_constant(my_agent, best_score);
4375  epmem_buffer_add_wme(meta_wmes, state->id.epmem_result_header, my_agent->epmem_sym_match_score, temp_sym);
4376  symbol_remove_ref(my_agent, temp_sym);
4377  // normalized match score
4378  temp_sym = make_float_constant(my_agent, best_score / perfect_score);
4379  epmem_buffer_add_wme(meta_wmes, state->id.epmem_result_header, my_agent->epmem_sym_normalized_match_score, temp_sym);
4380  symbol_remove_ref(my_agent, temp_sym);
4381  // status
4382  epmem_buffer_add_wme(meta_wmes, state->id.epmem_result_header, my_agent->epmem_sym_success, pos_query);
4383  if (neg_query) {
4384  epmem_buffer_add_wme(meta_wmes, state->id.epmem_result_header, my_agent->epmem_sym_success, neg_query);
4385  }
4386  // give more metadata if graph match is turned on
4387  if (do_graph_match) {
4388  // graph match
4389  temp_sym = make_int_constant(my_agent, (best_graph_matched ? 1 : 0));
4390  epmem_buffer_add_wme(meta_wmes, state->id.epmem_result_header, my_agent->epmem_sym_graph_match, temp_sym);
4391  symbol_remove_ref(my_agent, temp_sym);
4392 
4393  // mapping
4394  if (best_graph_matched) {
4396  // mapping identifier
4397  Symbol* mapping = make_new_identifier(my_agent, 'M', level);
4398  epmem_buffer_add_wme(meta_wmes, state->id.epmem_result_header, my_agent->epmem_sym_graph_match_mapping, mapping);
4399  symbol_remove_ref(my_agent, mapping);
4400 
4401  for (epmem_literal_node_pair_map::iterator iter = best_bindings.begin(); iter != best_bindings.end(); iter++) {
4402  if ((*iter).first->value_is_id) {
4403  // create the node
4404  temp_sym = make_new_identifier(my_agent, 'N', level);
4405  epmem_buffer_add_wme(meta_wmes, mapping, my_agent->epmem_sym_graph_match_mapping_node, temp_sym);
4406  symbol_remove_ref(my_agent, temp_sym);
4407  // point to the cue identifier
4408  epmem_buffer_add_wme(meta_wmes, temp_sym, my_agent->epmem_sym_graph_match_mapping_cue, (*iter).first->value_sym);
4409  // save the mapping point for the episode
4410  node_map_map[(*iter).second.second] = temp_sym;
4411  node_mem_map[(*iter).second.second] = NULL;
4412  }
4413  }
4414  }
4415  }
4416  // reconstruct the actual episode
4417  if (level > 2) {
4418  epmem_install_memory(my_agent, state, best_episode, meta_wmes, retrieval_wmes, &node_mem_map);
4419  }
4420  if (best_graph_matched) {
4421  for (epmem_id_mapping::iterator iter = node_mem_map.begin(); iter != node_mem_map.end(); iter++) {
4422  epmem_id_mapping::iterator map_iter = node_map_map.find((*iter).first);
4423  if (map_iter != node_map_map.end() && (*iter).second) {
4424  epmem_buffer_add_wme(meta_wmes, (*map_iter).second, my_agent->epmem_sym_retrieved, (*iter).second);
4425  }
4426  }
4427  }
4428  my_agent->epmem_timers->query_result->stop();
4429  }
4430  }
4431 
4432  // cleanup
4433  my_agent->epmem_timers->query_cleanup->start();
4434  for (epmem_interval_set::iterator iter = interval_cleanup.begin(); iter != interval_cleanup.end(); iter++) {
4435  epmem_interval* interval = *iter;
4436  if (interval->sql) {
4437  interval->sql->get_pool()->release(interval->sql);
4438  }
4439  free_with_pool(&(my_agent->epmem_interval_pool), interval);
4440  }
4441  for (int type = EPMEM_RIT_STATE_NODE; type <= EPMEM_RIT_STATE_EDGE; type++) {
4442  for (epmem_triple_pedge_map::iterator iter = pedge_caches[type].begin(); iter != pedge_caches[type].end(); iter++) {
4443  epmem_pedge* pedge = (*iter).second;
4444  if (pedge->sql) {
4445  pedge->sql->get_pool()->release(pedge->sql);
4446  }
4447  pedge->literals.~epmem_literal_set();
4448  free_with_pool(&(my_agent->epmem_pedge_pool), pedge);
4449  }
4450  for (epmem_triple_uedge_map::iterator iter = uedge_caches[type].begin(); iter != uedge_caches[type].end(); iter++) {
4451  epmem_uedge* uedge = (*iter).second;
4452  uedge->pedges.~epmem_pedge_set();
4453  free_with_pool(&(my_agent->epmem_uedge_pool), uedge);
4454  }
4455  }
4456  for (epmem_wme_literal_map::iterator iter = literal_cache.begin(); iter != literal_cache.end(); iter++) {
4457  epmem_literal* literal = (*iter).second;
4458  literal->parents.~epmem_literal_set();
4459  literal->children.~epmem_literal_set();
4460  literal->matches.~epmem_node_pair_set();
4461  literal->values.~epmem_node_int_map();
4462  free_with_pool(&(my_agent->epmem_literal_pool), literal);
4463  }
4464  my_agent->epmem_timers->query_cleanup->stop();
4465 
4466  my_agent->epmem_timers->query->stop();
4467 }
4468 
4471 // Visualization (epmem::viz)
4474 
4475 inline std::string _epmem_print_sti( epmem_node_id id )
4476 {
4477  std::string t1, t2;
4478 
4479  t1.assign( "<id" );
4480 
4481  to_string( id, t2 );
4482  t1.append( t2 );
4483  t1.append( ">" );
4484 
4485  return t1;
4486 }
4487 
4488 void epmem_print_episode( agent* my_agent, epmem_time_id memory_id, std::string* buf )
4489 {
4490  // if this is before the first episode, initialize db components
4491  if ( my_agent->epmem_db->get_status() == soar_module::disconnected )
4492  {
4493  epmem_init_db( my_agent );
4494  }
4495 
4496  // if bad memory, bail
4497  buf->clear();
4498  if ( ( memory_id == EPMEM_MEMID_NONE ) ||
4499  !epmem_valid_episode( my_agent, memory_id ) )
4500  {
4501  return;
4502  }
4503 
4504  // fill episode map
4505  std::map< epmem_node_id, std::string > ltis;
4506  std::map< epmem_node_id, std::map< std::string, std::list< std::string > > > ep;
4507  {
4509  std::string temp_s, temp_s2, temp_s3;
4510  double temp_d;
4511  int64_t temp_i;
4512 
4513  my_q = my_agent->epmem_stmts_graph->get_edges;
4514  {
4515  epmem_node_id q0;
4516  epmem_node_id q1;
4517  int64_t w_type;
4518  bool val_is_short_term;
4519 
4520  epmem_rit_prep_left_right( my_agent, memory_id, memory_id, &( my_agent->epmem_rit_state_graph[ EPMEM_RIT_STATE_EDGE ] ) );
4521 
4522  // query for edges
4523  my_q->bind_int( 1, memory_id );
4524  my_q->bind_int( 2, memory_id );
4525  my_q->bind_int( 3, memory_id );
4526  my_q->bind_int( 4, memory_id );
4527  my_q->bind_int( 5, memory_id );
4528  while ( my_q->execute() == soar_module::row )
4529  {
4530  q0 = my_q->column_int( 0 );
4531  q1 = my_q->column_int( 2 );
4532  w_type = my_q->column_int( 3 );
4533  val_is_short_term = ( my_q->column_type( 4 ) == soar_module::null_t );
4534 
4535  switch ( w_type )
4536  {
4538  temp_i = static_cast<int64_t>( my_q->column_int( 1 ) );
4539  to_string( temp_i, temp_s );
4540  break;
4541 
4543  temp_d = my_q->column_double( 1 );
4544  to_string( temp_d, temp_s );
4545  break;
4546 
4548  temp_s.assign( const_cast<char *>( reinterpret_cast<const char *>( my_q->column_text( 1 ) ) ) );
4549  break;
4550  }
4551 
4552  if ( val_is_short_term )
4553  {
4554  temp_s2 = _epmem_print_sti( q1 );
4555  }
4556  else
4557  {
4558  temp_s2.assign( "@" );
4559  temp_s2.push_back( static_cast< char >( my_q->column_int( 4 ) ) );
4560 
4561  temp_i = static_cast< uint64_t >( my_q->column_int( 5 ) );
4562  to_string( temp_i, temp_s3 );
4563  temp_s2.append( temp_s3 );
4564 
4565  ltis[ q1 ] = temp_s2;
4566  }
4567 
4568  ep[ q0 ][ temp_s ].push_back( temp_s2 );
4569  }
4570  my_q->reinitialize();
4571  epmem_rit_clear_left_right( my_agent );
4572  }
4573 
4574  my_q = my_agent->epmem_stmts_graph->get_nodes;
4575  {
4576  epmem_node_id parent_id;
4577  int64_t attr_type, value_type;
4578 
4579  epmem_rit_prep_left_right( my_agent, memory_id, memory_id, &( my_agent->epmem_rit_state_graph[ EPMEM_RIT_STATE_NODE ] ) );
4580 
4581  my_q->bind_int( 1, memory_id );
4582  my_q->bind_int( 2, memory_id );
4583  my_q->bind_int( 3, memory_id );
4584  my_q->bind_int( 4, memory_id );
4585  while ( my_q->execute() == soar_module::row )
4586  {
4587  parent_id = my_q->column_int( 1 );
4588  attr_type = my_q->column_int( 4 );
4589  value_type = my_q->column_int( 5 );
4590 
4591  switch ( attr_type )
4592  {
4594  temp_i = static_cast<int64_t>( my_q->column_int( 2 ) );
4595  to_string( temp_i, temp_s );
4596  break;
4597 
4599  temp_d = my_q->column_double( 2 );
4600  to_string( temp_d, temp_s );
4601  break;
4602 
4604  temp_s.assign( const_cast<char *>( reinterpret_cast<const char *>( my_q->column_text( 2 ) ) ) );
4605  break;
4606  }
4607 
4608  switch ( value_type )
4609  {
4611  temp_i = static_cast<int64_t>( my_q->column_int( 3 ) );
4612  to_string( temp_i, temp_s2 );
4613  break;
4614 
4616  temp_d = my_q->column_double( 3 );
4617  to_string( temp_d, temp_s2 );
4618  break;
4619 
4621  temp_s2.assign( const_cast<char *>( reinterpret_cast<const char *>( my_q->column_text( 3 ) ) ) );
4622  break;
4623  }
4624 
4625  ep[ parent_id ][ temp_s ].push_back( temp_s2 );
4626  }
4627  my_q->reinitialize();
4628  epmem_rit_clear_left_right( my_agent );
4629  }
4630  }
4631 
4632  // output
4633  {
4634  std::map< epmem_node_id, std::string >::iterator lti_it;
4635  std::map< epmem_node_id, std::map< std::string, std::list< std::string > > >::iterator ep_it;
4636  std::map< std::string, std::list< std::string > >::iterator slot_it;
4637  std::list< std::string >::iterator val_it;
4638 
4639  for ( ep_it=ep.begin(); ep_it!=ep.end(); ep_it++ )
4640  {
4641  buf->append( "(" );
4642 
4643  // id
4644  lti_it = ltis.find( ep_it->first );
4645  if ( lti_it == ltis.end() )
4646  {
4647  buf->append( _epmem_print_sti( ep_it->first ) );
4648  }
4649  else
4650  {
4651  buf->append( lti_it->second );
4652  }
4653 
4654  // attr
4655  for ( slot_it=ep_it->second.begin(); slot_it!=ep_it->second.end(); slot_it++ )
4656  {
4657  buf->append( " ^" );
4658  buf->append( slot_it->first );
4659 
4660  for ( val_it=slot_it->second.begin(); val_it!=slot_it->second.end(); val_it++ )
4661  {
4662  buf->append( " " );
4663  buf->append( *val_it );
4664  }
4665  }
4666 
4667  buf->append( ")\n" );
4668  }
4669  }
4670 }
4671 
4672 void epmem_visualize_episode( agent* my_agent, epmem_time_id memory_id, std::string* buf )
4673 {
4674  // if this is before the first episode, initialize db components
4675  if ( my_agent->epmem_db->get_status() == soar_module::disconnected )
4676  {
4677  epmem_init_db( my_agent );
4678  }
4679 
4680  // if bad memory, bail
4681  buf->clear();
4682  if ( ( memory_id == EPMEM_MEMID_NONE ) ||
4683  !epmem_valid_episode( my_agent, memory_id ) )
4684  {
4685  return;
4686  }
4687 
4688  // init
4689  {
4690  buf->append( "digraph epmem {\n" );
4691  }
4692 
4693  // taken heavily from install
4694  {
4696 
4697  // first identifiers (i.e. reconstruct)
4698  my_q = my_agent->epmem_stmts_graph->get_edges;
4699  {
4700  // for printing
4701  std::map< epmem_node_id, std::string > stis;
4702  std::map< epmem_node_id, std::pair< std::string, std::string > > ltis;
4703  std::list< std::string > edges;
4704  std::map< epmem_node_id, std::string >::iterator sti_p;
4705  std::map< epmem_node_id, std::pair< std::string, std::string > >::iterator lti_p;
4706 
4707  // relates to finite automata: q1 = d(q0, w)
4708  epmem_node_id q0; // id
4709  epmem_node_id q1; // attribute
4710  int64_t w_type; // we support any constant attribute symbol
4711  std::string temp, temp2, temp3, temp4;
4712  double temp_d;
4713  int64_t temp_i;
4714 
4715  bool val_is_short_term;
4716  char val_letter;
4717  uint64_t val_num;
4718 
4719  // 0 is magic
4720  temp.assign( "ID_0" );
4721  stis.insert( std::make_pair< epmem_node_id, std::string >( 0, temp ) );
4722 
4723  // prep rit
4724  epmem_rit_prep_left_right( my_agent, memory_id, memory_id, &( my_agent->epmem_rit_state_graph[ EPMEM_RIT_STATE_EDGE ] ) );
4725 
4726  // query for edges
4727  my_q->bind_int( 1, memory_id );
4728  my_q->bind_int( 2, memory_id );
4729  my_q->bind_int( 3, memory_id );
4730  my_q->bind_int( 4, memory_id );
4731  my_q->bind_int( 5, memory_id );
4732  while ( my_q->execute() == soar_module::row )
4733  {
4734  // q0, w, q1, w_type, letter, num
4735  q0 = my_q->column_int( 0 );
4736  q1 = my_q->column_int( 2 );
4737  w_type = my_q->column_int( 3 );
4738 
4739  // "ID_Q0"
4740  temp.assign( "ID_" );
4741  to_string( q0, temp2 );
4742  temp.append( temp2 );
4743 
4744  // "ID_Q1"
4745  temp3.assign( "ID_" );
4746  to_string( q1, temp2 );
4747  temp3.append( temp2 );
4748 
4749  val_is_short_term = ( my_q->column_type( 4 ) == soar_module::null_t );
4750  if ( val_is_short_term )
4751  {
4752  sti_p = stis.find( q1 );
4753  if ( sti_p == stis.end() )
4754  {
4755  stis.insert( std::make_pair< epmem_node_id, std::string >( q1, temp3 ) );
4756  }
4757  }
4758  else
4759  {
4760  lti_p = ltis.find( q1 );
4761 
4762  if ( lti_p == ltis.end() )
4763  {
4764  // "L#"
4765  val_letter = static_cast<char>( my_q->column_int( 4 ) );
4766  to_string( val_letter, temp4 );
4767  val_num = static_cast<uint64_t>( my_q->column_int( 5 ) );
4768  to_string( val_num, temp2 );
4769  temp4.append( temp2 );
4770 
4771  ltis.insert( std::make_pair< epmem_node_id, std::pair< std::string, std::string > >( q1, std::make_pair< std::string, std::string >( temp3, temp4 ) ) );
4772  }
4773  }
4774 
4775  // " -> ID_Q1"
4776  temp.append( " -> " );
4777  temp.append( temp3 );
4778 
4779  // " [ label="w" ];\n"
4780  temp.append( " [ label=\"" );
4781  switch ( w_type )
4782  {
4784  temp_i = static_cast<int64_t>( my_q->column_int( 1 ) );
4785  to_string( temp_i, temp2 );
4786  break;
4787 
4789  temp_d = my_q->column_double( 1 );
4790  to_string( temp_d, temp2 );
4791  break;
4792 
4794  temp2.assign( const_cast<char *>( reinterpret_cast<const char *>( my_q->column_text( 1 ) ) ) );
4795  break;
4796  }
4797  temp.append( temp2 );
4798  temp.append( "\" ];\n" );
4799 
4800  edges.push_back( temp );
4801  }
4802  my_q->reinitialize();
4803  epmem_rit_clear_left_right( my_agent );
4804 
4805  // identifiers
4806  {
4807  // short-term
4808  {
4809  buf->append( "node [ shape = circle ];\n" );
4810 
4811  for ( sti_p=stis.begin(); sti_p!=stis.end(); sti_p++ )
4812  {
4813  buf->append( sti_p->second );
4814  buf->append( " " );
4815  }
4816 
4817  buf->append( ";\n" );
4818  }
4819 
4820  // long-term
4821  {
4822  buf->append( "node [ shape = doublecircle ];\n" );
4823 
4824  for ( lti_p=ltis.begin(); lti_p!=ltis.end(); lti_p++ )
4825  {
4826  buf->append( lti_p->second.first );
4827  buf->append( " [ label=\"" );
4828  buf->append( lti_p->second.second );
4829  buf->append( "\" ];\n" );
4830  }
4831 
4832  buf->append( "\n" );
4833  }
4834  }
4835 
4836  // edges
4837  {
4838  std::list< std::string >::iterator e_p;
4839 
4840  for ( e_p=edges.begin(); e_p!=edges.end(); e_p++ )
4841  {
4842  buf->append( (*e_p) );
4843  }
4844  }
4845  }
4846 
4847  // then node_unique
4848  my_q = my_agent->epmem_stmts_graph->get_nodes;
4849  {
4850  epmem_node_id child_id;
4851  epmem_node_id parent_id;
4852  int64_t attr_type;
4853  int64_t value_type;
4854 
4855  std::list< std::string > edges;
4856  std::list< std::string > consts;
4857 
4858  std::string temp, temp2;
4859  double temp_d;
4860  int64_t temp_i;
4861 
4862  epmem_rit_prep_left_right( my_agent, memory_id, memory_id, &( my_agent->epmem_rit_state_graph[ EPMEM_RIT_STATE_NODE ] ) );
4863 
4864  my_q->bind_int( 1, memory_id );
4865  my_q->bind_int( 2, memory_id );
4866  my_q->bind_int( 3, memory_id );
4867  my_q->bind_int( 4, memory_id );
4868  while ( my_q->execute() == soar_module::row )
4869  {
4870  // f.child_id, f.parent_id, f.name, f.value, f.attr_type, f.value_type
4871  child_id = my_q->column_int( 0 );
4872  parent_id = my_q->column_int( 1 );
4873  attr_type = my_q->column_int( 4 );
4874  value_type = my_q->column_int( 5 );
4875 
4876  temp.assign( "ID_" );
4877  to_string( parent_id, temp2 );
4878  temp.append( temp2 );
4879  temp.append( " -> C_" );
4880  to_string( child_id, temp2 );
4881  temp.append( temp2 );
4882  temp.append( " [ label=\"" );
4883 
4884  // make a symbol to represent the attribute
4885  switch ( attr_type )
4886  {
4888  temp_i = static_cast<int64_t>( my_q->column_int( 2 ) );
4889  to_string( temp_i, temp2 );
4890  break;
4891 
4893  temp_d = my_q->column_double( 2 );
4894  to_string( temp_d, temp2 );
4895  break;
4896 
4898  temp2.assign( const_cast<char *>( reinterpret_cast<const char *>( my_q->column_text( 2 ) ) ) );
4899  break;
4900  }
4901 
4902  temp.append( temp2 );
4903  temp.append( "\" ];\n" );
4904  edges.push_back( temp );
4905 
4906  temp.assign( "C_" );
4907  to_string( child_id, temp2 );
4908  temp.append( temp2 );
4909  temp.append( " [ label=\"" );
4910 
4911  // make a symbol to represent the value
4912  switch ( value_type )
4913  {
4915  temp_i = static_cast<int64_t>( my_q->column_int( 3 ) );
4916  to_string( temp_i, temp2 );
4917  break;
4918 
4920  temp_d = my_q->column_double( 3 );
4921  to_string( temp_d, temp2 );
4922  break;
4923 
4925  temp2.assign( const_cast<char *>( reinterpret_cast<const char *>( my_q->column_text( 3 ) ) ) );
4926  break;
4927  }
4928 
4929  temp.append( temp2 );
4930  temp.append( "\" ];\n" );
4931 
4932  consts.push_back( temp );
4933 
4934  }
4935  my_q->reinitialize();
4936  epmem_rit_clear_left_right( my_agent );
4937 
4938  // constant nodes
4939  {
4940  std::list< std::string >::iterator e_p;
4941 
4942  buf->append( "node [ shape = plaintext ];\n" );
4943 
4944  for ( e_p=consts.begin(); e_p!=consts.end(); e_p++ )
4945  {
4946  buf->append( (*e_p) );
4947  }
4948  }
4949 
4950  // edges
4951  {
4952  std::list< std::string >::iterator e_p;
4953 
4954  for ( e_p=edges.begin(); e_p!=edges.end(); e_p++ )
4955  {
4956  buf->append( (*e_p) );
4957  }
4958  }
4959  }
4960  }
4961 
4962  // close
4963  {
4964  buf->append( "\n}\n" );
4965  }
4966 }
4967 
4970 // API Implementation (epmem::api)
4973 
4974 /***************************************************************************
4975  * Function : epmem_consider_new_episode
4976  * Author : Nate Derbinsky
4977  * Notes : Based upon trigger/force parameter settings, potentially
4978  * records a new episode
4979  **************************************************************************/
4981 {
4983  my_agent->epmem_timers->trigger->start();
4985 
4986  const int64_t force = my_agent->epmem_params->force->get_value();
4987  bool new_memory = false;
4988 
4989  if ( force == epmem_param_container::force_off )
4990  {
4991  const int64_t trigger = my_agent->epmem_params->trigger->get_value();
4992 
4993  if ( trigger == epmem_param_container::output )
4994  {
4995  slot *s;
4996  wme *w;
4997  Symbol *ol = my_agent->io_header_output;
4998 
4999  // examine all commands on the output-link for any
5000  // that appeared since last memory was recorded
5001  for ( s = ol->id.slots; s != NIL; s = s->next )
5002  {
5003  for ( w = s->wmes; w != NIL; w = w->next )
5004  {
5005  if ( w->timetag > my_agent->top_goal->id.epmem_info->last_ol_time )
5006  {
5007  new_memory = true;
5008  my_agent->top_goal->id.epmem_info->last_ol_time = w->timetag;
5009  }
5010  }
5011  }
5012  }
5013  else if ( trigger == epmem_param_container::dc )
5014  {
5015  new_memory = true;
5016  }
5017  else if ( trigger == epmem_param_container::none )
5018  {
5019  new_memory = false;
5020  }
5021  }
5022  else
5023  {
5024  new_memory = ( force == epmem_param_container::remember );
5025 
5027  }
5028 
5030  my_agent->epmem_timers->trigger->stop();
5032 
5033  if ( new_memory )
5034  {
5035  epmem_new_episode( my_agent );
5036  }
5037 
5038  return new_memory;
5039 }
5040 
5041 void inline _epmem_respond_to_cmd_parse( agent* my_agent, epmem_wme_list* cmds, bool& good_cue, int& path, epmem_time_id& retrieve, Symbol*& next, Symbol*& previous, Symbol*& query, Symbol*& neg_query, epmem_time_list& prohibit, epmem_time_id& before, epmem_time_id& after, epmem_symbol_set& currents, soar_module::wme_set& cue_wmes )
5042 {
5043  cue_wmes.clear();
5044 
5045  retrieve = EPMEM_MEMID_NONE;
5046  next = NULL;
5047  previous = NULL;
5048  query = NULL;
5049  neg_query = NULL;
5050  prohibit.clear();
5051  before = EPMEM_MEMID_NONE;
5052  after = EPMEM_MEMID_NONE;
5053  good_cue = true;
5054  path = 0;
5055 
5056  for ( epmem_wme_list::iterator w_p=cmds->begin(); w_p!=cmds->end(); w_p++ )
5057  {
5058  cue_wmes.insert( (*w_p) );
5059 
5060  if ( good_cue )
5061  {
5062  // collect information about known commands
5063  if ( (*w_p)->attr == my_agent->epmem_sym_retrieve )
5064  {
5065  if ( ( (*w_p)->value->ic.common_symbol_info.symbol_type == INT_CONSTANT_SYMBOL_TYPE ) &&
5066  ( path == 0 ) &&
5067  ( (*w_p)->value->ic.value > 0 ) )
5068  {
5069  retrieve = (*w_p)->value->ic.value;
5070  path = 1;
5071  }
5072  else
5073  {
5074  good_cue = false;
5075  }
5076  }
5077  else if ( (*w_p)->attr == my_agent->epmem_sym_next )
5078  {
5079  if ( ( (*w_p)->value->id.common_symbol_info.symbol_type == IDENTIFIER_SYMBOL_TYPE ) &&
5080  ( path == 0 ) )
5081  {
5082  next = (*w_p)->value;
5083  path = 2;
5084  }
5085  else
5086  {
5087  good_cue = false;
5088  }
5089  }
5090  else if ( (*w_p)->attr == my_agent->epmem_sym_prev )
5091  {
5092  if ( ( (*w_p)->value->id.common_symbol_info.symbol_type == IDENTIFIER_SYMBOL_TYPE ) &&
5093  ( path == 0 ) )
5094  {
5095  previous = (*w_p)->value;
5096  path = 2;
5097  }
5098  else
5099  {
5100  good_cue = false;
5101  }
5102  }
5103  else if ( (*w_p)->attr == my_agent->epmem_sym_query )
5104  {
5105  if ( ( (*w_p)->value->id.common_symbol_info.symbol_type == IDENTIFIER_SYMBOL_TYPE ) &&
5106  ( ( path == 0 ) || ( path == 3 ) ) &&
5107  ( query == NULL ) )
5108 
5109  {
5110  query = (*w_p)->value;
5111  path = 3;
5112  }
5113  else
5114  {
5115  good_cue = false;
5116  }
5117  }
5118  else if ( (*w_p)->attr == my_agent->epmem_sym_negquery )
5119  {
5120  if ( ( (*w_p)->value->id.common_symbol_info.symbol_type == IDENTIFIER_SYMBOL_TYPE ) &&
5121  ( ( path == 0 ) || ( path == 3 ) ) &&
5122  ( neg_query == NULL ) )
5123 
5124  {
5125  neg_query = (*w_p)->value;
5126  path = 3;
5127  }
5128  else
5129  {
5130  good_cue = false;
5131  }
5132  }
5133  else if ( (*w_p)->attr == my_agent->epmem_sym_before )
5134  {
5135  if ( ( (*w_p)->value->ic.common_symbol_info.symbol_type == INT_CONSTANT_SYMBOL_TYPE ) &&
5136  ( ( path == 0 ) || ( path == 3 ) ) )
5137  {
5138  if ( ( before == EPMEM_MEMID_NONE ) || ( (*w_p)->value->ic.value < before ) )
5139  {
5140  before = (*w_p)->value->ic.value;
5141  }
5142  path = 3;
5143  }
5144  else
5145  {
5146  good_cue = false;
5147  }
5148  }
5149  else if ( (*w_p)->attr == my_agent->epmem_sym_after )
5150  {
5151  if ( ( (*w_p)->value->ic.common_symbol_info.symbol_type == INT_CONSTANT_SYMBOL_TYPE ) &&
5152  ( ( path == 0 ) || ( path == 3 ) ) )
5153  {
5154  if ( after < (*w_p)->value->ic.value )
5155  {
5156  after = (*w_p)->value->ic.value;
5157  }
5158  path = 3;
5159  }
5160  else
5161  {
5162  good_cue = false;
5163  }
5164  }
5165  else if ( (*w_p)->attr == my_agent->epmem_sym_prohibit )
5166  {
5167  if ( ( (*w_p)->value->ic.common_symbol_info.symbol_type == INT_CONSTANT_SYMBOL_TYPE ) &&
5168  ( ( path == 0 ) || ( path == 3 ) ) )
5169  {
5170  prohibit.push_back( (*w_p)->value->ic.value );
5171  path = 3;
5172  }
5173  else
5174  {
5175  good_cue = false;
5176  }
5177  }
5178  else if ( (*w_p)->attr == my_agent->epmem_sym_current )
5179  {
5180  if ( ( (*w_p)->value->ic.common_symbol_info.symbol_type == IDENTIFIER_SYMBOL_TYPE ) &&
5181  ( ( path == 0 ) || ( path == 3 ) ) )
5182  {
5183  currents.insert( (*w_p)->value );
5184  path = 3;
5185  }
5186  else
5187  {
5188  good_cue = false;
5189  }
5190  }
5191  else
5192  {
5193  good_cue = false;
5194  }
5195  }
5196  }
5197 
5198  // if on path 3 must have query
5199  if ( ( path == 3 ) && ( query == NULL ) )
5200  {
5201  good_cue = false;
5202  }
5203 
5204  // must be on a path
5205  if ( path == 0 )
5206  {
5207  good_cue = false;
5208  }
5209 }
5210 
5211 /***************************************************************************
5212  * Function : epmem_respond_to_cmd
5213  * Author : Nate Derbinsky
5214  * Notes : Implements the Soar-EpMem API
5215  **************************************************************************/
5216 void epmem_respond_to_cmd( agent *my_agent )
5217 {
5218  // if this is before the first episode, initialize db components
5219  if ( my_agent->epmem_db->get_status() == soar_module::disconnected )
5220  {
5221  epmem_init_db( my_agent );
5222  }
5223 
5224  // respond to query only if db is properly initialized
5225  if ( my_agent->epmem_db->get_status() != soar_module::connected )
5226  {
5227  return;
5228  }
5229 
5230  // start at the bottom and work our way up
5231  // (could go in the opposite direction as well)
5232  Symbol *state = my_agent->bottom_goal;
5233 
5234  epmem_wme_list *wmes;
5235  epmem_wme_list *cmds;
5236  epmem_wme_list::iterator w_p;
5237 
5238  soar_module::wme_set cue_wmes;
5240  soar_module::symbol_triple_list retrieval_wmes;
5241 
5242  epmem_time_id retrieve;
5243  Symbol *next;
5244  Symbol *previous;
5245  Symbol *query;
5246  Symbol *neg_query;
5247  epmem_time_list prohibit;
5248  epmem_time_id before, after;
5249 #ifdef USE_MEM_POOL_ALLOCATORS
5250  epmem_symbol_set currents = epmem_symbol_set( std::less< Symbol* >(), soar_module::soar_memory_pool_allocator< Symbol* >( my_agent ) );
5251 #else
5252  epmem_symbol_set currents = epmem_symbol_set();
5253 #endif
5254  bool good_cue;
5255  int path;
5256 
5257  uint64_t wme_count;
5258  bool new_cue;
5259 
5260  bool do_wm_phase = false;
5261 
5262  while ( state != NULL )
5263  {
5265  my_agent->epmem_timers->api->start();
5267 
5268  // make sure this state has had some sort of change to the cmd
5269  new_cue = false;
5270  wme_count = 0;
5271  cmds = NULL;
5272  {
5273  tc_number tc = get_new_tc_number( my_agent );
5274  std::queue<Symbol *> syms;
5275  Symbol *parent_sym;
5276 
5277  // initialize BFS at command
5278  syms.push( state->id.epmem_cmd_header );
5279 
5280  while ( !syms.empty() )
5281  {
5282  // get state
5283  parent_sym = syms.front();
5284  syms.pop();
5285 
5286  // get children of the current identifier
5287  wmes = epmem_get_augs_of_id( parent_sym, tc );
5288  {
5289  for ( w_p=wmes->begin(); w_p!=wmes->end(); w_p++ )
5290  {
5291  wme_count++;
5292 
5293  if ( (*w_p)->timetag > state->id.epmem_info->last_cmd_time )
5294  {
5295  new_cue = true;
5296  state->id.epmem_info->last_cmd_time = (*w_p)->timetag;
5297  }
5298 
5299  if ( (*w_p)->value->common.symbol_type == IDENTIFIER_SYMBOL_TYPE )
5300  {
5301  syms.push( (*w_p)->value );
5302  }
5303  }
5304 
5305  // free space from aug list
5306  if ( cmds == NIL )
5307  {
5308  cmds = wmes;
5309  }
5310  else
5311  {
5312  delete wmes;
5313  }
5314  }
5315  }
5316 
5317  // see if any WMEs were removed
5318  if ( state->id.epmem_info->last_cmd_count != wme_count )
5319  {
5320  new_cue = true;
5321  state->id.epmem_info->last_cmd_count = wme_count;
5322  }
5323 
5324  if ( new_cue )
5325  {
5326  // clear old results
5327  epmem_clear_result( my_agent, state );
5328 
5329  do_wm_phase = true;
5330  }
5331  }
5332 
5333  // a command is issued if the cue is new
5334  // and there is something on the cue
5335  if ( new_cue && wme_count )
5336  {
5337  _epmem_respond_to_cmd_parse( my_agent, cmds, good_cue, path, retrieve, next, previous, query, neg_query, prohibit, before, after, currents, cue_wmes );
5338 
5340  my_agent->epmem_timers->api->stop();
5342 
5343  retrieval_wmes.clear();
5344  meta_wmes.clear();
5345 
5346  // process command
5347  if ( good_cue )
5348  {
5349  // retrieve
5350  if ( path == 1 )
5351  {
5352  epmem_install_memory( my_agent, state, retrieve, meta_wmes, retrieval_wmes );
5353  }
5354  // previous or next
5355  else if ( path == 2 )
5356  {
5357  if ( next )
5358  {
5359  epmem_install_memory( my_agent, state, epmem_next_episode( my_agent, state->id.epmem_info->last_memory ), meta_wmes, retrieval_wmes );
5360 
5361  // add one to the next stat
5362  my_agent->epmem_stats->nexts->set_value( my_agent->epmem_stats->nexts->get_value() + 1 );
5363  }
5364  else
5365  {
5366  epmem_install_memory( my_agent, state, epmem_previous_episode( my_agent, state->id.epmem_info->last_memory ), meta_wmes, retrieval_wmes );
5367 
5368  // add one to the prev stat
5369  my_agent->epmem_stats->prevs->set_value( my_agent->epmem_stats->prevs->get_value() + 1 );
5370  }
5371 
5372  if ( state->id.epmem_info->last_memory == EPMEM_MEMID_NONE )
5373  {
5374  epmem_buffer_add_wme( meta_wmes, state->id.epmem_result_header, my_agent->epmem_sym_failure, ( ( next )?( next ):( previous ) ) );
5375  }
5376  else
5377  {
5378  epmem_buffer_add_wme( meta_wmes, state->id.epmem_result_header, my_agent->epmem_sym_success, ( ( next )?( next ):( previous ) ) );
5379  }
5380  }
5381  // query
5382  else if ( path == 3 )
5383  {
5384  epmem_process_query( my_agent, state, query, neg_query, prohibit, before, after, currents, cue_wmes, meta_wmes, retrieval_wmes );
5385 
5386  // add one to the cbr stat
5387  my_agent->epmem_stats->cbr->set_value( my_agent->epmem_stats->cbr->get_value() + 1 );
5388  }
5389  }
5390  else
5391  {
5392  epmem_buffer_add_wme( meta_wmes, state->id.epmem_result_header, my_agent->epmem_sym_status, my_agent->epmem_sym_bad_cmd );
5393  }
5394 
5395  // clear prohibit list
5396  prohibit.clear();
5397 
5398  if ( !retrieval_wmes.empty() || !meta_wmes.empty() )
5399  {
5400  // process preference assertion en masse
5401  epmem_process_buffered_wmes( my_agent, state, cue_wmes, meta_wmes, retrieval_wmes );
5402 
5403  // clear cache
5404  {
5405  soar_module::symbol_triple_list::iterator mw_it;
5406 
5407  for ( mw_it=retrieval_wmes.begin(); mw_it!=retrieval_wmes.end(); mw_it++ )
5408  {
5409  symbol_remove_ref( my_agent, (*mw_it)->id );
5410  symbol_remove_ref( my_agent, (*mw_it)->attr );
5411  symbol_remove_ref( my_agent, (*mw_it)->value );
5412 
5413  delete (*mw_it);
5414  }
5415  retrieval_wmes.clear();
5416 
5417  for ( mw_it=meta_wmes.begin(); mw_it!=meta_wmes.end(); mw_it++ )
5418  {
5419  symbol_remove_ref( my_agent, (*mw_it)->id );
5420  symbol_remove_ref( my_agent, (*mw_it)->attr );
5421  symbol_remove_ref( my_agent, (*mw_it)->value );
5422 
5423  delete (*mw_it);
5424  }
5425  meta_wmes.clear();
5426  }
5427 
5428  // process wm changes on this state
5429  do_wm_phase = true;
5430  }
5431 
5432  // clear cue wmes
5433  cue_wmes.clear();
5434  }
5435  else
5436  {
5438  my_agent->epmem_timers->api->stop();
5440  }
5441 
5442  // free space from command aug list
5443  if ( cmds )
5444  {
5445  delete cmds;
5446  }
5447 
5448  state = state->id.higher_goal;
5449  }
5450 
5451  if ( do_wm_phase )
5452  {
5454  my_agent->epmem_timers->wm_phase->start();
5456 
5457  do_working_memory_phase( my_agent );
5458 
5460  my_agent->epmem_timers->wm_phase->stop();
5462  }
5463 }
5464 
5465 #ifdef EPMEM_EXPERIMENT
5466 void inline _epmem_exp( agent* my_agent )
5467 {
5468  // hopefully generally useful code for evaluating
5469  // query speed as number of episodes increases
5470  // usage: top-state.epmem.queries <q>
5471  // <q> ^reps #
5472  // ^mod # (optional, defaults to 1)
5473  // ^output |filename|
5474  // ^format << csv speedy >>
5475  // ^features <fs>
5476  // <fs> ^|key| |value| # note: value must be a string, not int or float; wrap it in pipes (eg. |0|)
5477  // ^commands <cmds>
5478  // <cmds> ^|label| <cmd>
5479 
5480  if ( !epmem_exp_timer )
5481  {
5482  epmem_exp_timer = new soar_module::timer( "exp", my_agent, soar_module::timer::zero, new soar_module::predicate<soar_module::timer::timer_level>(), false );
5483  }
5484 
5485  double c1;
5486 
5487  epmem_exp_timer->reset();
5488  epmem_exp_timer->start();
5489  epmem_dc_wme_adds = -1;
5490  bool new_episode = epmem_consider_new_episode( my_agent );
5491  epmem_exp_timer->stop();
5492  c1 = epmem_exp_timer->value();
5493 
5494  if ( new_episode )
5495  {
5496  Symbol* queries = make_sym_constant( my_agent, "queries" );
5497  Symbol* reps = make_sym_constant( my_agent, "reps" );
5498  Symbol* mod = make_sym_constant( my_agent, "mod" );
5499  Symbol* output = make_sym_constant( my_agent, "output" );
5500  Symbol* format = make_sym_constant( my_agent, "format" );
5501  Symbol* features = make_sym_constant( my_agent, "features" );
5502  Symbol* cmds = make_sym_constant( my_agent, "commands" );
5503  Symbol* csv = make_sym_constant( my_agent, "csv" );
5504 
5505  slot* queries_slot = find_slot( my_agent->top_goal->id.epmem_header, queries );
5506  if ( queries_slot )
5507  {
5508  wme* queries_wme = queries_slot->wmes;
5509 
5510  if ( queries_wme->value->common.symbol_type == IDENTIFIER_SYMBOL_TYPE )
5511  {
5512  Symbol* queries_id = queries_wme->value;
5513  slot* reps_slot = find_slot( queries_id, reps );
5514  slot* mod_slot = find_slot( queries_id, mod );
5515  slot* output_slot = find_slot( queries_id, output );
5516  slot* format_slot = find_slot( queries_id, format );
5517  slot* features_slot = find_slot( queries_id, features );
5518  slot* commands_slot = find_slot( queries_id, cmds );
5519 
5520  if ( reps_slot && output_slot && format_slot && commands_slot )
5521  {
5522  wme* reps_wme = reps_slot->wmes;
5523  wme* mod_wme = ( ( mod_slot )?( mod_slot->wmes ):( NULL ) );
5524  wme* output_wme = output_slot->wmes;
5525  wme* format_wme = format_slot->wmes;
5526  wme* features_wme = ( ( features_slot )?( features_slot->wmes ):( NULL ) );
5527  wme* commands_wme = commands_slot->wmes;
5528 
5529  if ( ( reps_wme->value->common.symbol_type == INT_CONSTANT_SYMBOL_TYPE ) &&
5530  ( output_wme->value->common.symbol_type == SYM_CONSTANT_SYMBOL_TYPE ) &&
5531  ( format_wme->value->common.symbol_type == SYM_CONSTANT_SYMBOL_TYPE ) &&
5532  ( commands_wme->value->common.symbol_type == IDENTIFIER_SYMBOL_TYPE ) )
5533  {
5534  int64_t reps = reps_wme->value->ic.value;
5535  int64_t mod = ( ( mod_wme && ( mod_wme->value->common.symbol_type == INT_CONSTANT_SYMBOL_TYPE ) )?( mod_wme->value->ic.value ):(1) );
5536  const char* output_fname = output_wme->value->sc.name;
5537  bool format_csv = ( format_wme->value == csv );
5538  std::list< std::pair< std::string, std::string > > output_contents;
5539  std::string temp_str, temp_str2;
5540  std::set< std::string > cmd_names;
5541 
5542  std::map< std::string, std::string > features;
5543  if ( features_wme && features_wme->value->common.symbol_type == IDENTIFIER_SYMBOL_TYPE )
5544  {
5545  for ( slot* s=features_wme->value->id.slots; s; s=s->next )
5546  {
5547  for ( wme* w=s->wmes; w; w=w->next )
5548  {
5549  if ( ( w->attr->common.symbol_type == SYM_CONSTANT_SYMBOL_TYPE ) &&
5550  ( w->value->common.symbol_type == SYM_CONSTANT_SYMBOL_TYPE ) )
5551  {
5552  features[ w->attr->sc.name ] = w->value->sc.name;
5553  }
5554  }
5555  }
5556  }
5557 
5558  if ( ( mod == 1 ) || ( ( ( my_agent->epmem_stats->time->get_value()-1 ) % mod ) == 1 ) )
5559  {
5560 
5561  // all fields (used to produce csv header), possibly stub values at this point
5562  {
5563  // features
5564  for ( std::map< std::string, std::string >::iterator f_it=features.begin(); f_it!=features.end(); f_it++ )
5565  {
5566  output_contents.push_back( std::make_pair< std::string, std::string >( f_it->first, f_it->second ) );
5567  }
5568 
5569  // decision
5570  {
5571  to_string( my_agent->d_cycle_count, temp_str );
5572  output_contents.push_back( std::make_pair< std::string, std::string >( "dc", temp_str ) );
5573  }
5574 
5575  // episode number
5576  {
5577  to_string( my_agent->epmem_stats->time->get_value()-1, temp_str );
5578  output_contents.push_back( std::make_pair< std::string, std::string >( "episodes", temp_str ) );
5579  }
5580 
5581  // reps
5582  {
5583  to_string( reps, temp_str );
5584  output_contents.push_back( std::make_pair< std::string, std::string >( "reps", temp_str ) );
5585  }
5586 
5587  // current wm size
5588  {
5589  to_string( my_agent->num_wmes_in_rete, temp_str );
5590  output_contents.push_back( std::make_pair< std::string, std::string >( "wmcurrent", temp_str ) );
5591  }
5592 
5593  // wm adds/removes
5594  {
5595  to_string( ( my_agent->wme_addition_count - epmem_exp_state[ exp_state_wm_adds ] ), temp_str );
5596  output_contents.push_back( std::make_pair< std::string, std::string >( "wmadds", temp_str ) );
5597  epmem_exp_state[ exp_state_wm_adds ] = static_cast< int64_t >( my_agent->wme_addition_count );
5598 
5599  to_string( ( my_agent->wme_removal_count - epmem_exp_state[ exp_state_wm_removes ] ), temp_str );
5600  output_contents.push_back( std::make_pair< std::string, std::string >( "wmremoves", temp_str ) );
5601  epmem_exp_state[ exp_state_wm_removes ] = static_cast< int64_t >( my_agent->wme_removal_count );
5602  }
5603 
5604  // dc interval add/removes
5605  {
5606  to_string( epmem_dc_interval_inserts, temp_str );
5607  output_contents.push_back( std::make_pair< std::string, std::string >( "dcintervalinserts", temp_str ) );
5608 
5609  to_string( epmem_dc_interval_removes, temp_str );
5610  output_contents.push_back( std::make_pair< std::string, std::string >( "dcintervalremoves", temp_str ) );
5611  }
5612 
5613  // dc wme adds
5614  {
5615  to_string( epmem_dc_wme_adds, temp_str );
5616  output_contents.push_back( std::make_pair< std::string, std::string >( "dcwmeadds", temp_str ) );
5617  }
5618 
5619  // sqlite memory
5620  {
5621  int64_t sqlite_mem = my_agent->epmem_stats->mem_usage->get_value();
5622 
5623  to_string( ( sqlite_mem - epmem_exp_state[ exp_state_sqlite_mem ] ), temp_str );
5624  output_contents.push_back( std::make_pair< std::string, std::string >( "sqlitememadd", temp_str ) );
5625  epmem_exp_state[ exp_state_sqlite_mem ] = sqlite_mem;
5626 
5627  to_string( sqlite_mem, temp_str );
5628  output_contents.push_back( std::make_pair< std::string, std::string >( "sqlitememcurrent", temp_str ) );
5629  }
5630 
5631  // storage time in seconds
5632  {
5633  to_string( c1, temp_str );
5634 
5635  output_contents.push_back( std::make_pair< std::string, std::string >( "storage", temp_str ) );
5636  cmd_names.insert( "storage" );
5637  }
5638 
5639  // commands
5640  for ( slot* s=commands_wme->value->id.slots; s; s=s->next )
5641  {
5642  output_contents.push_back( std::make_pair< std::string, std::string >( s->attr->sc.name, "" ) );
5643  std::string searched = "numsearched";
5644  searched.append(s->attr->sc.name);
5645  output_contents.push_back( std::make_pair< std::string, std::string >( searched , "" ) );
5646  }
5647  }
5648 
5649  // open file, write header
5650  if ( !epmem_exp_output )
5651  {
5652  epmem_exp_output = new std::ofstream( output_fname );
5653 
5654  if ( format_csv )
5655  {
5656  for ( std::list< std::pair< std::string, std::string > >::iterator it=output_contents.begin(); it!=output_contents.end(); it++ )
5657  {
5658  if ( it != output_contents.begin() )
5659  {
5660  (*epmem_exp_output) << ",";
5661  }
5662 
5663  (*epmem_exp_output) << "\"" << it->first << "\"";
5664  }
5665 
5666  (*epmem_exp_output) << std::endl;
5667  }
5668  }
5669 
5670  // collect timing data
5671  {
5672  epmem_wme_list* cmds = NULL;
5673  soar_module::wme_set cue_wmes;
5675  soar_module::symbol_triple_list retrieval_wmes;
5676 
5677  epmem_time_id retrieve;
5678  Symbol* next;
5679  Symbol* previous;
5680  Symbol* query;
5681  Symbol* neg_query;
5682  epmem_time_list prohibit;
5683  epmem_time_id before, after;
5684 #ifdef USE_MEM_POOL_ALLOCATORS
5685  epmem_symbol_set currents = epmem_symbol_set( std::less< Symbol* >(), soar_module::soar_memory_pool_allocator< Symbol* >( my_agent ) );
5686 #else
5687  epmem_symbol_set currents = epmem_symbol_set();
5688 #endif
5689  bool good_cue;
5690  int path;
5691 
5692  //
5693 
5694  for ( slot* s=commands_wme->value->id.slots; s; s=s->next )
5695  {
5696  if ( s->wmes->value->common.symbol_type == IDENTIFIER_SYMBOL_TYPE )
5697  {
5698  // parse command once
5699  {
5700  cmds = epmem_get_augs_of_id( s->wmes->value, get_new_tc_number( my_agent ) );
5701  _epmem_respond_to_cmd_parse( my_agent, cmds, good_cue, path, retrieve, next, previous, query, neg_query, prohibit, before, after, currents, cue_wmes );
5702  }
5703 
5704  if ( good_cue && ( path == 3 ) )
5705  {
5706  // execute lots of times
5707  double c_total = 0;
5708  {
5709  epmem_exp_timer->reset();
5710  epmem_exp_timer->start();
5711  for ( int64_t i=1; i<=reps; i++ )
5712  {
5713  epmem_process_query( my_agent, my_agent->top_goal, query, neg_query, prohibit, before, after, currents, cue_wmes, meta_wmes, retrieval_wmes, 2 );
5714 
5715  if ( !retrieval_wmes.empty() || !meta_wmes.empty() )
5716  {
5717  soar_module::symbol_triple_list::iterator mw_it;
5718 
5719  for ( mw_it=retrieval_wmes.begin(); mw_it!=retrieval_wmes.end(); mw_it++ )
5720  {
5721  symbol_remove_ref( my_agent, (*mw_it)->id );
5722  symbol_remove_ref( my_agent, (*mw_it)->attr );
5723  symbol_remove_ref( my_agent, (*mw_it)->value );
5724 
5725  delete (*mw_it);
5726  }
5727  retrieval_wmes.clear();
5728 
5729  for ( mw_it=meta_wmes.begin(); mw_it!=meta_wmes.end(); mw_it++ )
5730  {
5731  symbol_remove_ref( my_agent, (*mw_it)->id );
5732  symbol_remove_ref( my_agent, (*mw_it)->attr );
5733  symbol_remove_ref( my_agent, (*mw_it)->value );
5734 
5735  delete (*mw_it);
5736  }
5737  meta_wmes.clear();
5738  }
5739  }
5740  epmem_exp_timer->stop();
5741  c_total = epmem_exp_timer->value();
5742  }
5743 
5744  // update results
5745  {
5746  to_string( c_total, temp_str );
5747 
5748  for ( std::list< std::pair< std::string, std::string > >::iterator oc_it=output_contents.begin(); oc_it!=output_contents.end(); oc_it++ )
5749  {
5750  if ( oc_it->first.compare( s->attr->sc.name ) == 0 )
5751  {
5752  oc_it->second.assign( temp_str );
5753  oc_it++;
5754  to_string( epmem_episodes_searched , temp_str );
5755  oc_it->second.assign( temp_str );
5756  }
5757  }
5758 
5759  cmd_names.insert( s->attr->sc.name );
5760  }
5761  }
5762 
5763  // clean
5764  {
5765  delete cmds;
5766  }
5767  }
5768  }
5769  }
5770 
5771  // output data
5772  if ( format_csv )
5773  {
5774  for ( std::list< std::pair< std::string, std::string > >::iterator it=output_contents.begin(); it!=output_contents.end(); it++ )
5775  {
5776  if ( it != output_contents.begin() )
5777  {
5778  (*epmem_exp_output) << ",";
5779  }
5780 
5781  (*epmem_exp_output) << "\"" << it->second << "\"";
5782  }
5783 
5784  (*epmem_exp_output) << std::endl;
5785  }
5786  else
5787  {
5788  for ( std::set< std::string >::iterator c_it=cmd_names.begin(); c_it!=cmd_names.end(); c_it++ )
5789  {
5790  for ( std::list< std::pair< std::string, std::string > >::iterator it=output_contents.begin(); it!=output_contents.end(); it++ )
5791  {
5792  if ( cmd_names.find( it->first ) == cmd_names.end() )
5793  {
5794  if ( it->first.substr( 0, 11 ).compare( "numsearched" ) == 0 )
5795  {
5796  continue;
5797  }
5798 
5799  if ( it != output_contents.begin() )
5800  {
5801  (*epmem_exp_output) << " ";
5802  }
5803  if ( ( it->first.compare( "reps" ) == 0 ) && ( c_it->compare( "storage" ) == 0 ) )
5804  {
5805  (*epmem_exp_output) << it->first << "=" << "1";
5806  }
5807  else
5808  {
5809  (*epmem_exp_output) << it->first << "=" << it->second;
5810  }
5811  }
5812  else if ( c_it->compare( it->first ) == 0 )
5813  {
5814  (*epmem_exp_output) << " command=" << it->first << " totalsec=" << it->second;
5815  if ( it->first.compare( "storage" ) == 0 ) {
5816  (*epmem_exp_output) << " numsearched=0";
5817  break;
5818  } else {
5819  it++;
5820  (*epmem_exp_output) << " numsearched=" << it->second;
5821  break;
5822  }
5823  }
5824  }
5825 
5826  (*epmem_exp_output) << std::endl;
5827  }
5828  }
5829  }
5830  }
5831  }
5832  }
5833  }
5834 
5835  symbol_remove_ref( my_agent, queries );
5836  symbol_remove_ref( my_agent, reps );
5837  symbol_remove_ref( my_agent, mod );
5838  symbol_remove_ref( my_agent, output );
5839  symbol_remove_ref( my_agent, format );
5840  symbol_remove_ref( my_agent, features );
5841  symbol_remove_ref( my_agent, cmds );
5842  symbol_remove_ref( my_agent, csv );
5843  }
5844 }
5845 #endif
5846 
5847 /***************************************************************************
5848  * Function : epmem_go
5849  * Author : Nate Derbinsky
5850  * Notes : The kernel calls this function to implement Soar-EpMem:
5851  * consider new storage and respond to any commands
5852  **************************************************************************/
5853 void epmem_go( agent *my_agent, bool allow_store )
5854 {
5855 
5856  my_agent->epmem_timers->total->start();
5857 
5858 #ifndef EPMEM_EXPERIMENT
5859 
5860  if ( allow_store )
5861  {
5862  epmem_consider_new_episode( my_agent );
5863  }
5864  epmem_respond_to_cmd( my_agent );
5865 
5866 #else // EPMEM_EXPERIMENT
5867 
5868  _epmem_exp( my_agent );
5869  epmem_respond_to_cmd( my_agent );
5870 
5871 #endif // EPMEM_EXPERIMENT
5872 
5873  my_agent->epmem_timers->total->stop();
5874 
5875 }
5876 
5877 bool epmem_backup_db( agent* my_agent, const char* file_name, std::string *err )
5878 {
5879  bool return_val = false;
5880 
5881  if ( my_agent->epmem_db->get_status() == soar_module::connected )
5882  {
5883  if ( my_agent->epmem_params->lazy_commit->get_value() == soar_module::on )
5884  {
5886  }
5887 
5888  return_val = my_agent->epmem_db->backup( file_name, err );
5889 
5890  if ( my_agent->epmem_params->lazy_commit->get_value() == soar_module::on )
5891  {
5893  }
5894  }
5895  else
5896  {
5897  err->assign( "Episodic database is not currently connected." );
5898  }
5899 
5900  return return_val;
5901 }