#ifndef RULE_SESSION_H_ #define RULE_SESSION_H_ #include "rdf_rule_core.h" #include "rdf_rule_cback.h" #include "rdf_index.h" #include "rdf_graph.h" #include "rdf_session.h" #include "backward_reasoner.h" #include "rule_internal.h" #include "rule_term_base.h" #include "knowledge_base.h" #include "rule_explain_why.h" #include "rule_stat_collector.h" namespace rule { namespace internal { template class rule_term; }; /* internal namespace */ std::string get_backward_chaining_rule_name(knowledge_base const& kbase, internal::rule_vertex const root_vertex); // ============================================================================================================ // CLASSES FOR QUERY RULE // ============================================================================================================ class query_rule_wrapper; ///////////////////////////////////////////////////////////////////////////////////////// // class query_params_map // ///////////////////////////////////////////////////////////////////////////////////////// class query_params_map { public: typedef std::tr1::unordered_map params_t; query_params_map(): m_params() {}; ~query_params_map(){}; inline void add_param(std::string name, rdf::index_type value){m_params.insert(params_t::value_type(name, value));}; inline void insert(std::string name, rdf::index_type value){m_params.insert(params_t::value_type(name, value));}; inline void clear(){m_params.clear();}; inline params_t::const_iterator get_params_begin()const{return m_params.begin();}; inline params_t::const_iterator get_params_end() const{return m_params.end();}; private: params_t m_params; }; // utils functions - defined in rule_utils.cpp void print_beta_relation(std::string const& message, internal::rule_vertex const v, rule_session const* session); void print_relation_row(rule_session const* session, internal::beta_relation::const_iterator const& r_itor); ///////////////////////////////////////////////////////////////////////////////////////// // class priority_consequent_type // // Define a priority_queue of the rule_graph (of Graph Vertexes) ///////////////////////////////////////////////////////////////////////////////////////// struct priority_consequent_type { priority_consequent_type(unsigned int p, internal::rule_vertex v, internal::relation_row_ptr_type const& r): priority(p), vertex(v), row_p(r) {}; unsigned int priority; internal::rule_vertex vertex; internal::relation_row_ptr_type row_p; }; typedef std::tr1::shared_ptr priority_consequent_ptr_type; struct less_pc { inline bool operator()(priority_consequent_type const& lhs, priority_consequent_type const& rhs)const { return lhs.priority < rhs.priority; }; }; struct less_ptr_pc { inline bool operator()(priority_consequent_ptr_type const& lhs, priority_consequent_ptr_type const& rhs)const { return lhs->priority < rhs->priority; }; }; typedef std::priority_queue, less_ptr_pc> consequent_queue_type; class rule_session; typedef std::tr1::shared_ptr rule_session_ptr_type; ///////////////////////////////////////////////////////////////////////////////////////// // class rule_session // ///////////////////////////////////////////////////////////////////////////////////////// class rule_session { typedef std::tr1::shared_ptr beta_relation_ptr_type; typedef std::vector beta_relation_map_type; typedef std::vector rule_counter_map_type; typedef consequent_queue_type visited_consequent_queue_type; typedef std::tr1::unordered_set pkg_rule_vertices_set_t; typedef std::tr1::unordered_set backw_rule_vertex_set_t; typedef std::tr1::unordered_map explain_vertex_lookup_map_type; typedef std::tr1::unordered_multimap explain_lookup_map_type; public: typedef rule_execution_log_type::const_iterator rule_events_const_iterator_type; typedef rdf::top_iterator rule_event_iterator_t; rule_session( rdf::rdf_session *session_p, knowledge_base const& kbase); inline explicit rule_session(rule_session const& rhs) : m_relations(rhs.m_relations), m_visited_consequents(rhs.m_visited_consequents), m_rule_counter_map(rhs.m_rule_counter_map), m_is_log_rule_events(rhs.m_is_log_rule_events), m_rule_log(rhs.m_rule_log), m_explain_lookup_map(rhs.m_explain_lookup_map), m_rdf_session_p(rhs.m_rdf_session_p), m_reasoners_pkg_scheduled(rhs.m_reasoners_pkg_scheduled), m_reasoners_scheduled(rhs.m_reasoners_scheduled), m_reasoners_executed(rhs.m_reasoners_executed), m_owl_onProperty(), m_owl_maxCardinality(), m_owl_minCardinality(), m_owl_cardinality(), m_owl_sameAs(), m_owl_true(), m_owl_false(), m_kbase(rhs.m_kbase), m_verbose(rhs.m_verbose), m_rule_stat_collector_p(), m_max_rule_visit(rhs.m_max_rule_visit) {}; inline index_type get_index_type(std::string const& name)const { return m_rdf_session_p->get_index_type(name); }; inline bool is_verbose()const { return m_verbose; }; inline void set_verbose(bool b) { m_verbose = b; }; // ============================================================================================================ // logging_rule_events methods // // This is required for explain why facility. // ============================================================================================================ inline bool is_logging_rule_events()const { return m_is_log_rule_events; }; inline void set_logging_rule_events(bool const b) { m_is_log_rule_events = b; if(m_is_log_rule_events) m_explain_lookup_map.rehash(50); }; protected: inline void log_rule_event(rule_event_ptr_type const& event_p) { m_rule_log.push_back(event_p); }; inline void log_inferred_triple(rdf::index_type const u, rdf::index_type const v, rdf::index_type const w) { if(!is_logging_rule_events()) return; rule_event_ptr_type rule_event_p = get_last_event(); rule_event_p->add_triple(u, v, w); m_explain_lookup_map.insert( explain_lookup_map_type::value_type(rdf::index_triple(u, v, w), rule_event_p)); }; inline rule_event_ptr_type get_last_event()const { if(!is_logging_rule_events()) throw rdf::rdf_exception(rdf::none, "rule_session::get_last_event: ERROR: called while is_logging_rule_events is false!"); return m_rule_log.back(); }; inline void pop_last_event() { if(!is_logging_rule_events()) throw rdf::rdf_exception(rdf::none, "rule_session::pop_last_event: ERROR: called while is_logging_rule_events is false!"); m_rule_log.pop_back(); }; public: inline rule_events_const_iterator_type get_rule_events_begin()const { return m_rule_log.begin(); }; inline rule_events_const_iterator_type get_rule_events_end()const { return m_rule_log.end(); }; inline rule_event_iterator_t get_rule_event_iterator()const { return rule_event_iterator_t(get_rule_events_begin(), get_rule_events_end()); }; inline void clear_rule_events() { m_rule_log.clear(); m_explain_lookup_map.clear(); }; inline void print_rule_events() { rule_events_const_iterator_type itor = get_rule_events_begin(); rule_events_const_iterator_type end = get_rule_events_end(); while(itor != end) { (*itor)->print_event(); ++itor; } }; // ============================================================================================================ inline internal::beta_relation const& get_beta_relation(internal::rule_vertex v)const { beta_relation_ptr_type const& r = m_relations[v]; if(!r) { throw rdf::rdf_exception(rdf::invalid_index, "rule_session::get_beta_relation: invalid rule vertex"); }; return *r; }; internal::beta_relation& get_beta_relation(internal::rule_vertex v); inline rdf::rdf_session & get_rdf_session() {return *m_rdf_session_p;}; inline rdf::rdf_session const& get_rdf_session()const{return *m_rdf_session_p;}; // called by rule_engine void initialize(); // ============================================================================================================ // METHOD USED BY BINARY-OPERATORS FOR OWL LANGUAGE // ============================================================================================================ // returns false if fails owl max cardinality test w/ (c rdf:type owl:Restriction) bool test_max_cardinality(rdf::index_type const s, rdf::index_type const c); bool test_min_cardinality(rdf::index_type const s, rdf::index_type const c); bool test_cardinality(rdf::index_type const s, rdf::index_type const c); // ============================================================================================================ // ONLY METHOD THAT SHOULD BE CALLED BY USER TO INVOKE RULES // This will invoke all triggered backward chaining rules // ============================================================================================================ void execute_rules(); void execute_rules(internal::rule_vertex const from_vertex, bool apply_consequents); void execute_retract_rules(internal::rule_vertex const from_vertex); template void execute_rules_internal(Propagator const& propagator, internal::rule_vertex const from_vertex, bool apply_consequents); void apply_consequent_rules(); void retract_consequent_rules(); // ============================================================================================================ // MEMBER FUNCTIONS TO INVOKE BACKWARD CHAINING RULES // ============================================================================================================ inline bool has_backward_chaining_rule_fired(internal::rule_vertex v)const { return m_reasoners_executed.find(v) != m_reasoners_executed.end(); }; internal::back_reasoner_mgr const& get_back_reasoner_mgr()const; inline void schedule_reasoners(internal::pkg_rule_vertices_ptr_t const& backw_p) { if(m_reasoners_pkg_scheduled.find(&*backw_p) != m_reasoners_pkg_scheduled.end()) return; if(m_verbose) { std::cout << "rule_session::schedule_reasoners: scheduling pkg reasoner: " << &*backw_p << std::endl; } internal::pkg_rule_vertices_t::const_iterator itor = backw_p->begin(); internal::pkg_rule_vertices_t::const_iterator end = backw_p->end(); for(; itor!=end; ++itor) { if(!has_backward_chaining_rule_fired(*itor)) { if(m_verbose) { std::cout << "rule_session::schedule_reasoners: scheduling backward chaining rule: " << get_backward_chaining_rule_name(m_kbase, *itor) << std::endl; } m_reasoners_scheduled.insert(*itor); } } m_reasoners_pkg_scheduled.insert(&*backw_p); }; // defined in rule_session.cpp void execute_scheduled_reasoners(); ///////////////////////////////////////////////////////////////////////////////////////// // add_consequent_row // ///////////////////////////////////////////////////////////////////////////////////////// inline void add_consequent_row(unsigned int priority, internal::rule_vertex v, internal::relation_row_ptr_type const& row_p) { if(m_max_rule_visit and priority and ++m_rule_counter_map[v]>=m_max_rule_visit) throw rdf::rdf_exception(rdf::rule_infinite_loop, "rule_session::consequent_visited: infinite loop in rule detected"); if(priority) m_visited_consequents.push(priority_consequent_ptr_type(new priority_consequent_type(priority, v, row_p))); }; inline bool has_visited_consequents() { return !m_visited_consequents.empty(); }; inline priority_consequent_ptr_type const& top_priority_visited_consequent() { return m_visited_consequents.top(); }; inline void pop_visited_consequent() { m_visited_consequents.pop(); }; ///////////////////////////////////////////////////////////////////////////////////////// // ============================================================================================================ // query execution methods // ============================================================================================================ query_rule_wrapper get_query(std::string const& name); query_rule_wrapper get_query(std::string const& name, query_params_map const& params); // ------------------------------------------------------------------------------------------------------------- // ============================================================================================================ // explain why methods - requires the flag is_logging_rule_events set to true to be used. // // Explain why facility is to be used in conjuction with relatively small data set since it is not optimized. // All inferred triples are put into an inefficient multi-map as key with value being the row of beta relation // that inferred the triple. // // The fairly large data structure holding the mapping between inferred triples and source relation row is // computed on demand upon the first call to explain_why by the method initialize_explain_why(). // ============================================================================================================ explain_ptr_type explain_why(rdf::index_type const s, rdf::index_type const p, rdf::index_type const o); explain_ptr_type explain_why(std::string const& s, std::string const& p, std::string const& o) { return explain_why(m_rdf_session_p->get_index_type(s), m_rdf_session_p->get_index_type(p), m_rdf_session_p->get_index_type(o)); }; explain_ptr_type explain_why(unsigned int s, unsigned int p, unsigned int o) { return explain_why(m_rdf_session_p->get_index_type(s), m_rdf_session_p->get_index_type(p), m_rdf_session_p->get_index_type(o)); }; // ============================================================================================================ // debugging methods // // access to rule terms and knowledge base to print status and debug information while inferring // ============================================================================================================ void print_beta_relations()const; inline knowledge_base const& get_knowledge_base()const { return m_kbase; }; inline knowledge_term const& get_knowledge_term(internal::rule_vertex const u)const { return m_kbase.get_knowledge_term(u); }; inline void set_rule_stat_collector_ptr(rule_stat_collector_ptr_type const& c) { m_rule_stat_collector_p = c; }; inline rule_stat_collector_ptr_type get_rule_stat_collector_ptr()const { return m_rule_stat_collector_p; }; private: void explain_why_graph(explain_graph_type & graph, explain_vertex_lookup_map_type & lookup_map, explain_vertex const root_vertex, rdf::index_triple const explain_triple); protected: templatefriend class internal::rule_term; friend void rule::print_beta_relation(std::string const& message, internal::rule_vertex const v, rule_session const* session_p); private: beta_relation_map_type m_relations; visited_consequent_queue_type m_visited_consequents; rule_counter_map_type m_rule_counter_map; bool m_is_log_rule_events; rule_execution_log_type m_rule_log; explain_lookup_map_type m_explain_lookup_map; rdf::rdf_session * m_rdf_session_p; pkg_rule_vertices_set_t m_reasoners_pkg_scheduled; backw_rule_vertex_set_t m_reasoners_scheduled; backw_rule_vertex_set_t m_reasoners_executed; rdf::index_type m_owl_onProperty; rdf::index_type m_owl_maxCardinality; rdf::index_type m_owl_minCardinality; rdf::index_type m_owl_cardinality; rdf::index_type m_owl_sameAs; rdf::index_type m_owl_true; rdf::index_type m_owl_false; knowledge_base const& m_kbase; bool m_verbose; rule_stat_collector_ptr_type m_rule_stat_collector_p; unsigned int m_max_rule_visit; }; ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // rule_session::execute_rule // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template void rule_session::execute_rules_internal(Propagator const& propagator, internal::rule_vertex const from_vertex, bool apply_consequents) { if(m_verbose) std::cout << "\n>> Executing forward chaining rules . . .\n"; try { propagator(this, from_vertex); } catch(rdf::rdf_exception const& e) { std::cout << "rule_session::execute_rules: Exception caught while executing forward chaining rules, message: " << e.what() << std::endl; if(e.code() == rdf::rule_infinite_loop) std::cout << "Infinite loop detected in forward chaining rules" << std::endl; throw e; } catch(...) { const char* msg = "rule_session::execute_rules: Unknown (general) Exception caught while executing forward chaining rules!!, no message available"; std::cout << msg << std::endl; throw rdf::rdf_exception(rdf::none, msg); } // fire requested backward chaining rules if(m_verbose) std::cout << "\n>> Executing dependent backward chaining rules . . .\n"; try { execute_scheduled_reasoners(); } catch(rdf::rdf_exception const& e) { std::cout << "rule_session::execute_rules: Exception caught while executing backward chaining rules, message: " << e.what() << std::endl; if(e.code() == rdf::rule_infinite_loop) std::cout << "Infinite loop detected in backward chaining rules" << std::endl; throw e; } catch(...) { const char * msg = "rule_session::execute_rules: Unknown (general) Exception caught while executing backward chaining rules!!, no message available"; std::cout << msg << std::endl; throw rdf::rdf_exception(rdf::none, msg); } // apply the consequent rule in order of rule salience if(apply_consequents) { if(m_verbose) std::cout << "\n>> Applying all consequent terms . . .\n"; try { apply_consequent_rules(); } catch(rdf::rdf_exception const& e) { std::cout << "rule_session::execute_rules: Exception caught while executing consequent terms of rules, message: " << e.what() << std::endl; if(e.code() == rdf::rule_infinite_loop) std::cout << "Infinite loop detected in consequent terms of rules" << std::endl; throw e; } catch(...) { const char * msg = "rule_session::execute_rules: Unknown (general) Exception caught while executing consequent terms of rules!!, no message available"; std::cout << msg << std::endl; throw rdf::rdf_exception(rdf::none, msg); } } }; namespace internal { ///////////////////////////////////////////////////////////////////////////////////////// // rule_term_base::add_row // // defined in rule_session.h due to dependency and to ensure inlining ///////////////////////////////////////////////////////////////////////////////////////// inline unsigned int rule_term_base::add_row(rule_session * rule_session_p, relation_row_map const& relation_row, rdf::index_type const* triple, beta_relation &relation)const { relation_row_ptr_type row_p = relation.create_relation_row(); std::size_t hash_code = initialize_row(row_p, relation_row, triple); // check if we filter out the row - apply_filter returns true if expression is false (ie. do not keep row) if(apply_filter(rule_session_p, *row_p)) return 0; return relation.insert_row(rule_session_p, row_p, hash_code); }; ///////////////////////////////////////////////////////////////////////////////////////// // rule_term_base::remove_row // // defined in rule_session.h due to dependency and to ensure inlining ///////////////////////////////////////////////////////////////////////////////////////// inline unsigned int rule_term_base::remove_row(rule_session * rule_session_p, relation_row_map const& relation_row, rdf::index_type const* triple, beta_relation &relation)const { relation_row_ptr_type row_p = relation.create_relation_row(); initialize_row(row_p, relation_row, triple); std::size_t hash_code = initialize_row(row_p, relation_row, triple); // check if we filter out the row if(apply_filter(rule_session_p, *row_p)) return 0; return relation.delete_row(rule_session_p, row_p, hash_code); }; /////////////////////////////////////////////////////// // print_row_internal // // utility method used in beta_relation::insert_row and beta_relation::delete_row // special case: may be called with rule_session_p == NULL when adding head row, see rule_session.cpp /////////////////////////////////////////////////////////// inline void print_row_internal(std::string row_msg, std::string beta_msg, rule_session * rule_session_p, relation_row_ptr_type const& row_p, rule_vertex u, bool is_consequent) { if(!rule_session_p or !rule_session_p->is_verbose()) return; if(!is_consequent) return; knowledge_term const& kterm = rule_session_p->get_knowledge_term(u); std::string const& name = kterm.get_name(); rule_term_ptr_type const& rule_term_p = kterm.get_rule_term(); std::cout << row_msg << name << ": " << row_p->to_string(*rule_term_p) << std::endl; // if(is_consequent) print_beta_relation(beta_msg, u, rule_session_p); }; /////////////////////////////////////////////////////// // beta_relation::insert_row // // defined in rule_session.h due to dependency on rule_session and to ensure inlining. // special case: may be called with rule_session_p == NULL when adding head row, see rule_session.cpp /////////////////////////////////////////////////////////// inline unsigned int beta_relation::insert_row(rule_session * rule_session_p, relation_row_ptr_type const& row_p, std::size_t hash_code) { row_p->set_hash(hash_code); row_set_type::iterator iter; bool inserted; boost::tie(iter, inserted) = m_unique_row_set.insert(row_p); if(inserted) { // new row inserted // add row to the rule_session processing queux if(m_is_consequent_term) { row_p->set_inserted(); if(rule_session_p) rule_session_p->add_consequent_row(m_priority, m_rule_vertex, row_p); } else { row_p->set_processed(); } m_propagation_qx_rows.push_front(row_p); insert_lookup_index(row_p); //* print_row_internal("\t add_row at ", "beta_relation is now - ", rule_session_p, row_p, m_rule_vertex, m_is_consequent_term); return 1; } // row already exist - check status: // if marked for deletion, then cancel if((*iter)->is_deleted()) { (*iter)->set_processed(); // keeping the row afterall, retain the index lookup m_propagation_qx_rows.push_front(row_p); insert_lookup_index(row_p); //* print_row_internal("\t cancel row delete at ", "beta_relation is now - ", rule_session_p, row_p, m_rule_vertex, m_is_consequent_term); } else { //* print_row_internal("\t row already exist at ", "beta_relation is - ", rule_session_p, row_p, m_rule_vertex, m_is_consequent_term); } return 0; }; /////////////////////////////////////////////////////// // beta_relation::delete_row // // defined in rule_session.h due to dependency on rule_session and to ensure inlining. /////////////////////////////////////////////////////////// inline unsigned int beta_relation::delete_row(rule_session * rule_session_p, relation_row_ptr_type const& row_p, std::size_t hash_code) { row_p->set_hash(hash_code); row_set_type::iterator ipos = m_unique_row_set.find(row_p); if(ipos == m_unique_row_set.end()) { //* print_row_internal("\t already deleted! at ", "beta_relation is - ", rule_session_p, row_p, m_rule_vertex, m_is_consequent_term); return 0; // already deleted! } if((*ipos)->is_deleted()) { //* print_row_internal("\t already marked for deletion at ", "beta_relation is - ", rule_session_p, row_p, m_rule_vertex, m_is_consequent_term); return 0; // just about to be deleted } // If row is status inserted, then remove from queue and don't put it for delete either // although it will remain in the work_qx_rows, it's status will indicate otherwise. // // note: case of body term, unprocess rows in inserted qx will not be deleted in flight // since propagating the rete-network completes before the next add/delete event. // Therefore iterator will not be affected. // if(m_is_consequent_term) { if((*ipos)->is_inserted()) { // row was marked for insertion, cancel this and remove it (*ipos)->set_processed(); // put it in the propagation work queux in case this rule term is reused in other rule // in which case it's not a consequent term m_propagation_qx_rows.push_front(*ipos); remove_lookup_index(*ipos); m_unique_row_set.erase(ipos); //* print_row_internal("\t cancel insertion at ", "beta_relation is now - ", rule_session_p, row_p, m_rule_vertex, m_is_consequent_term); return 0; } // row status must be in processed state - need to put it up for delete (*ipos)->set_deleted(); // put it in the propagation work queux in case this rule term is reused in other rule // in which case it's not a consequent term m_propagation_qx_rows.push_front(*ipos); remove_lookup_index(*ipos); if(rule_session_p) rule_session_p->add_consequent_row(m_priority, m_rule_vertex, *ipos); } else { // case body term, delete the row and put it in work queue for rete network (*ipos)->set_processed(); m_propagation_qx_rows.push_front(*ipos); remove_lookup_index(*ipos); m_unique_row_set.erase(ipos); } //* print_row_internal("\t remove_row at ", "beta_relation is now - ", rule_session_p, row_p, m_rule_vertex, m_is_consequent_term); return 1; }; }; /* internal namespace */ }; /* rule namespace */ #endif /*RULE_SESSION_H_*/