#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 U, class V, class W, class Fu, class Fv, class Fw, class Operation>
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<std::string, rdf::index_type> 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_type> 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<priority_consequent_ptr_type, std::vector<priority_consequent_ptr_type>, less_ptr_pc> consequent_queue_type;
class rule_session;
typedef std::tr1::shared_ptr<rule_session> rule_session_ptr_type;
/////////////////////////////////////////////////////////////////////////////////////////
// class rule_session
//
/////////////////////////////////////////////////////////////////////////////////////////
class rule_session
{
typedef std::tr1::shared_ptr<internal::beta_relation> beta_relation_ptr_type;
typedef std::vector<beta_relation_ptr_type> beta_relation_map_type;
typedef std::vector<internal::rule_vertex> rule_counter_map_type;
typedef consequent_queue_type visited_consequent_queue_type;
typedef std::tr1::unordered_set<internal::pkg_rule_vertices_t*> pkg_rule_vertices_set_t;
typedef std::tr1::unordered_set<internal::rule_vertex> backw_rule_vertex_set_t;
typedef std::tr1::unordered_map<rdf::index_triple,
explain_vertex,
rdf::hash_index_triple,
rdf::eq_index_triple> explain_vertex_lookup_map_type;
typedef std::tr1::unordered_multimap<rdf::index_triple,
rule_event_ptr_type,
rdf::hash_index_triple,
rdf::eq_index_triple> explain_lookup_map_type;
public:
typedef rule_execution_log_type::const_iterator rule_events_const_iterator_type;
typedef rdf::top_iterator<rule_events_const_iterator_type> 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<class Propagator>
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:
template<class U, class V, class W, class Fu, class Fv, class Fw, class Operation>friend 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<class Propagator>
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_*/