C++ API tutorial

From OpenCog
Jump to: navigation, search

Using C++ for opencog is possible, but not generally recommended. This is because the whole point of OpenCog is to represent knowledge in terms of hypergraphs in the atomspace, and to manipulate and grow that knowledge using deduction, PLN, learning and various evolutionary algorithms. Of course, writing new evolutionary algorithms can mean writing in C++; and certainly fixing existing low-level, core code requires working with C++. However, newcomers to OpenCog are strongly encouraged to think in terms of hypergraphs, and representing their ideas in terms of hypergraphs, and in terms of the first-order logic that the PLN and pattern-matching engines can process.

Although most new code should really be "new hypgraphs", and new patterns (such as the SatisfactionLink and the BindLink), sometimes its just plain hard to avoid writing new algorithms. For this, scheme is recommended. In other cases, one might need to implement a new control process, aka a new "MindAgent" that controls inferencing, or possibly interfaces to external systems and sensors. For this, using python is recommended.

In the following tutorials, we will look at how to use existing atomspace API for CRUD operation and involving forward and backward chaining.

Atomspace CRUD operations

The following subsections show how to do CRUD operations in opencog. The Atomspace API header file may be referred for further study of available functions.

Inserting new atoms

Below is a code snippet that demonstrates the above concepts.

    void create_atoms(Atomspace& as){
         //Create atom with default TV         
         Handle h1 = atomSpace->addNode(CONCEPT_NODE, "ConceptNode1");
         //Create atom with a TV value 
         SimpleTruthValue tv1(0.5f, 0.99f);
         Handle h2 = atomSpace->add_node(CONCEPT_NODE, "ConceptNode2", tv1); 
                 
    }

Note: An atom could also be shared across multiple atomspaces in the case of Nested atomspaces(see Multiple atom spaces).

Retrieval

   // Get elements of a Link 
   HandleSeq outgoing = LinkCast(hinheritance)->getOutgoingSet();
   // Get all links pointing to a Node 
   opencog::IncomingSet incoming = NodeCast(h)->getIncomingSet();
   // Get all atoms of type T
   HandleSeq hs;   
   get_handles_by_type(std::back_inserter(hs),
                        INHERITANCE_LINK); // populates hs with the given type

Deletion

 
    // Set the recursive delete flag to delete h including everything pointing to it.
    // if the flag is not set and the atom has an incoming element, it won't be deleted.        
    as.remove_atom(h, true);

Unified Rule Engine

The Unified rule engine is opencog's generic rule engine that is used for PLN and other domain specific inferences via a forward or backward chaining algorithm using a predefined rule base. In the following sections we will try to cover basic concepts of forward and bckward chaining with code examples.

Forward chaining

Forward chaining is one of the two main methods of reasoning when using an inference engine and can be described logically as repeated application of modus ponens... see for more.

Forward chaining is an inference process where one starts with an Initial source, knowledge base (the atomspace in opencog lingua) and a set of rules called the rule base. The goal of it being creating new knowledge by selecting and applying appropriate rules matching the source to the knowledge base. The opencog forward chainer is implemented as an iterative single step forward chaining where a new source is selected each time from among potential sources and applied to a predefined set of atoms called the focus set. The termination criteria could be based on some fitness function or a predefined maximum iteration which the is the current approach. In order to Invoke the forward chainer we need to define a rule base first as shown here. Next step will be constructing the Forward chainer object shown the following code snippet:

  #include <opencog/atomspace/AtomSpace.h> 
  #include <opencog/util/Config.h>
  #include <opencog/rule-engine/forwardchainer/ForwardChainer.h>
  #include <opencog/guile/load-file.h>
  #include <opencog/guile/SchemeEval.h>

  void fc_demo(AtomSpace& as){
    //Make sure your rule base and knowledges are loaded to the atomspace.
    config().set("SCM_PRELOAD", "examples/c++-api/scm/deduction.scm, "
                 "examples/c++-api/scm/fc-simple-assertions.scm, "
                 "examples/c++-api/scm/fc-config.scm");
    load_scm_files_from_config (as);

    SchemeEval eval(&as);

    //Choose source atoms to apply forward chaining on.
    Handle source =
            eval.eval_h(
                    R"((ConceptNode "Socrates"))");

    //Get the handle to the rule base name exactly as defined in the rule base scm file.
    Handle rbs = as.get_node(CONCEPT_NODE, "FC_DEMO_RB");

    //The final argument to ForwardChainer constructor is used to pass
    //focus set atoms; when the focus set is not empty, the forward chainer
    //is constrained to apply rules only on the focus set atoms and applies
    //on the entire atomspace when focus set is empty.
    ForwardChainer fc(as, rbs,source, HandleSeq { });

    //Start chaining.
    //fc.do_step(); // For Single step forward chaining.
    fc.do_chain();  // Does Max_Iteration steps of forward chaining.
     
    //Get the results of your single step or full steps FCing
     HandleSeq fc_results = fc.get_chaining_results();
     
     //Some more processing here
    }

Backward chaining

In backward chaining, we are interested in either truth value fulfillment query or variable fulfillment query. For variable fulfillment query, variable containing link is passed as an argument and the backward chainer tries to find grounding for the variable. For truth value fulfillment query, the truth value of the original target are updated via inference. The main difference with forward chaining being multiple targets emerging while grounding variables in the premises of rules in an effort to grounding the variables of the initial target. Backward chaining is terminated with some termination criteria that is passed dynamically(dynamicaly passing termination criteria is not yet supported) where the current default being maximum iterations. Below is a code snippet showing how to invoke the backward chainer code.


void bc_demo(AtomSpace& as)
{
    config().set("SCM_PRELOAD", "examples/c++-api/scm/bc-criminal.scm,"
                 "examples/c++-api/scm/bc-config.scm");
    load_scm_files_from_config(as);

    SchemeEval eval(&as);
    Handle target_var = eval.eval_h("(VariableNode \"$who\")");
    Handle target = eval.eval_h("(InheritanceLink"
                                "   (VariableNode \"$who\")"
                                "   (ConceptNode \"criminal\"))");

    //Create BackwardChainer object.
    Handle hrbase = as.get_node(CONCEPT_NODE, "BC_DEMO_RB");
    BackwardChainer bc(as, hrbase);

    bc.set_target(target);
    //Set maximum number of iteration(backward chaining steps).
    //See http://wiki.opencog.org/w/Unified_Rule_Engine#Overall_Backward_Chaining_Process
    bc.get_config().set_maximum_iterations(1000);
    bc.do_chain();

    VarMultimap results = bc.get_chaining_result();

    std::cout << "Query:\n" << target->toShortString() << std::endl;
    std::cout << "Answer:\n";
    for (const auto& h : results[target_var])
        std::cout << h->toShortString() << std::endl;
}

The source code for the above topics is available on github.