<commit_before>/*
 * PatternMatch.cc
 *
 * Copyright (C) 2009, 2014, 2015 <NAME>
 *
 * Author: <NAME> <<EMAIL>>  January 2009
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License v3 as
 * published by the Free Software Foundation and including the exceptions
 * at http://opencog.org/wiki/Licenses
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, write to:
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include <opencog/util/Logger.h>

#include <opencog/atoms/pattern/BindLink.h>
#include <opencog/atoms/pattern/PatternLink.h>

#include <opencog/atomspace/AtomSpace.h>
#include <opencog/atomspace/ClassServer.h>

#include "PatternMatch.h"
#include "PatternMatchEngine.h"
#include "PatternMatchCallback.h"
#include "DefaultPatternMatchCB.h"

using namespace opencog;

/* ================================================================= */
/// A pass-through class, which wraps a regular callback, but captures
/// all of the different possible groundings that result.  This class is
/// used to piece together graphs out of multiple components.
class PMCGroundings : public PatternMatchCallback
{
	private:
		PatternMatchCallback& _cb;

	public:
		PMCGroundings(PatternMatchCallback& cb) : _cb(cb) {}

		// Pass all the calls straight through, except one.
		bool node_match(const Handle& node1, const Handle& node2) {
			return _cb.node_match(node1, node2);
		}
		bool variable_match(const Handle& node1, const Handle& node2) {
			return _cb.variable_match(node1, node2);
		}
		bool link_match(const LinkPtr& link1, const LinkPtr& link2) {
			return _cb.link_match(link1, link2);
		}
		bool post_link_match(const LinkPtr& link1, const LinkPtr& link2) {
			return _cb.post_link_match(link1, link2);
		}
		bool fuzzy_match(const Handle& h1, const Handle& h2) {
			return _cb.fuzzy_match(h1, h2);
		}
		bool evaluate_sentence(const Handle& link_h,
		                       const std::map<Handle, Handle> &gnds) {
			throw InvalidParamException(TRACE_INFO,
			              "Not expecting a virtual link here!");
		}
		bool clause_match(const Handle& pattrn_link_h, const Handle& grnd_link_h) {
			return _cb.clause_match(pattrn_link_h, grnd_link_h);
		}
		bool optional_clause_match(const Handle& pattrn, const Handle& grnd) {
			return _cb.optional_clause_match(pattrn, grnd);
		}
		IncomingSet get_incoming_set(const Handle& h) {
			return _cb.get_incoming_set(h);
		}
		void push(void) { _cb.push(); }
		void pop(void) { _cb.pop(); }
		void set_pattern(const Variables& vars,
	                    const Pattern& pat)
		{
			_cb.set_pattern(vars, pat);
		}

		bool initiate_search(PatternMatchEngine* pme)
		{
			return _cb.initiate_search(pme);
		}

		bool search_finished(bool done)
		{
			return _cb.search_finished(done);
		}

		// This one we don't pass through. Instead, we collect the
		// groundings.
		bool grounding(const std::map<Handle, Handle> &var_soln,
		               const std::map<Handle, Handle> &term_soln)
		{
			_term_groundings.push_back(term_soln);
			_var_groundings.push_back(var_soln);
			return false;
		}

		std::vector<std::map<Handle, Handle>> _term_groundings;
		std::vector<std::map<Handle, Handle>> _var_groundings;
};

/**
 * Recursive evaluator/grounder/unifier of virtual link types.
 * The virtual links are in 'virtuals', a partial set of groundings
 * are in 'var_gnds' and 'term_gnds', and a collection of possible
 * groundings for disconnected graph components are in 'comp_var_gnds'
 * and 'comp_term_gnds'.
 *
 * Notes below explain the recursive step: how the various disconnected
 * components are brought together into a candidate grounding. That
 * candidate is then run through each of the virtual links.  If these
 * accept the grounding, then the callback is called to make the final
 * determination.
 *
 * The recursion step terminates when comp_var_gnds, comp_term_gnds
 * are empty, at which point the actual unification is done.
 */
bool PatternMatch::recursive_virtual(PatternMatchCallback& cb,
            const std::vector<Handle>& virtuals,
            const std::vector<Handle>& negations, // currently ignored
            const std::map<Handle, Handle>& var_gnds,
            const std::map<Handle, Handle>& term_gnds,
            // copies, NOT references!
            std::vector<std::vector<std::map<Handle, Handle>>> comp_var_gnds,
            std::vector<std::vector<std::map<Handle, Handle>>> comp_term_gnds)
{
	// If we are done with the recursive step, then we have one of the
	// many combinatoric possibilities in the var_gnds and term_gnds
	// maps. Submit this grounding map to the virtual links, and see
	// what they've got to say about it.
	if (0 == comp_var_gnds.size())
	{
		if (logger().isFineEnabled())
		{
			logger().fine("\nExplore one possible combinatoric grounding "
			              "(var_gnds.size = %zu, term_gnds.size = %zu):\n",
			              var_gnds.size(), term_gnds.size());
			PatternMatchEngine::log_solution(var_gnds, term_gnds);
		}

		// Note, FYI, that if there are no virtual clauses at all,
		// then this loop falls straight-through, and the grounding
		// is reported as a match to the callback.  That is, the
		// virtuals only serve to reject possibilities.
		for (const Handle& virt : virtuals)
		{
			// At this time, we expect all virtual links to be in
			// one of two forms: either EvaluationLink's or
			// GreaterThanLink's. The EvaluationLinks should have
			// the structure
			//
			//   EvaluationLink
			//       GroundedPredicateNode "scm:blah"
			//       ListLink
			//           Arg1Atom
			//           Arg2Atom
			//
			// The GreaterThanLink's should have the "obvious" structure
			//
			//   GreaterThanLink
			//       Arg1Atom
			//       Arg2Atom
			//
			// In either case, one or more VariableNodes should appear
			// in the Arg atoms. So, we ground the args, and pass that<commit_msg>Minor code and log formatting fix<commit_after>/*
 * PatternMatch.cc
 *
 * Copyright (C) 2009, 2014, 2015 <NAME>
 *
 * Author: <NAME> <<EMAIL>>  January 2009
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License v3 as
 * published by the Free Software Foundation and including the exceptions
 * at http://opencog.org/wiki/Licenses
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, write to:
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include <opencog/util/Logger.h>

#include <opencog/atoms/pattern/BindLink.h>
#include <opencog/atoms/pattern/PatternLink.h>

#include <opencog/atomspace/AtomSpace.h>
#include <opencog/atomspace/ClassServer.h>

#include "PatternMatch.h"
#include "PatternMatchEngine.h"
#include "PatternMatchCallback.h"
#include "DefaultPatternMatchCB.h"

using namespace opencog;

/* ================================================================= */
/// A pass-through class, which wraps a regular callback, but captures
/// all of the different possible groundings that result.  This class is
/// used to piece together graphs out of multiple components.
class PMCGroundings : public PatternMatchCallback
{
	private:
		PatternMatchCallback& _cb;

	public:
		PMCGroundings(PatternMatchCallback& cb) : _cb(cb) {}

		// Pass all the calls straight through, except one.
		bool node_match(const Handle& node1, const Handle& node2) {
			return _cb.node_match(node1, node2);
		}
		bool variable_match(const Handle& node1, const Handle& node2) {
			return _cb.variable_match(node1, node2);
		}
		bool link_match(const LinkPtr& link1, const LinkPtr& link2) {
			return _cb.link_match(link1, link2);
		}
		bool post_link_match(const LinkPtr& link1, const LinkPtr& link2) {
			return _cb.post_link_match(link1, link2);
		}
		bool fuzzy_match(const Handle& h1, const Handle& h2) {
			return _cb.fuzzy_match(h1, h2);
		}
		bool evaluate_sentence(const Handle& link_h,
		                       const std::map<Handle, Handle> &gnds) {
			throw InvalidParamException(TRACE_INFO,
			              "Not expecting a virtual link here!");
		}
		bool clause_match(const Handle& pattrn_link_h, const Handle& grnd_link_h) {
			return _cb.clause_match(pattrn_link_h, grnd_link_h);
		}
		bool optional_clause_match(const Handle& pattrn, const Handle& grnd) {
			return _cb.optional_clause_match(pattrn, grnd);
		}
		IncomingSet get_incoming_set(const Handle& h) {
			return _cb.get_incoming_set(h);
		}
		void push(void) { _cb.push(); }
		void pop(void) { _cb.pop(); }
		void set_pattern(const Variables& vars,
		                 const Pattern& pat)
		{
			_cb.set_pattern(vars, pat);
		}

		bool initiate_search(PatternMatchEngine* pme)
		{
			return _cb.initiate_search(pme);
		}

		bool search_finished(bool done)
		{
			return _cb.search_finished(done);
		}

		// This one we don't pass through. Instead, we collect the
		// groundings.
		bool grounding(const std::map<Handle, Handle> &var_soln,
		               const std::map<Handle, Handle> &term_soln)
		{
			_term_groundings.push_back(term_soln);
			_var_groundings.push_back(var_soln);
			return false;
		}

		std::vector<std::map<Handle, Handle>> _term_groundings;
		std::vector<std::map<Handle, Handle>> _var_groundings;
};

/**
 * Recursive evaluator/grounder/unifier of virtual link types.
 * The virtual links are in 'virtuals', a partial set of groundings
 * are in 'var_gnds' and 'term_gnds', and a collection of possible
 * groundings for disconnected graph components are in 'comp_var_gnds'
 * and 'comp_term_gnds'.
 *
 * Notes below explain the recursive step: how the various disconnected
 * components are brought together into a candidate grounding. That
 * candidate is then run through each of the virtual links.  If these
 * accept the grounding, then the callback is called to make the final
 * determination.
 *
 * The recursion step terminates when comp_var_gnds, comp_term_gnds
 * are empty, at which point the actual unification is done.
 */
bool PatternMatch::recursive_virtual(PatternMatchCallback& cb,
            const std::vector<Handle>& virtuals,
            const std::vector<Handle>& negations, // currently ignored
            const std::map<Handle, Handle>& var_gnds,
            const std::map<Handle, Handle>& term_gnds,
            // copies, NOT references!
            std::vector<std::vector<std::map<Handle, Handle>>> comp_var_gnds,
            std::vector<std::vector<std::map<Handle, Handle>>> comp_term_gnds)
{
	// If we are done with the recursive step, then we have one of the
	// many combinatoric possibilities in the var_gnds and term_gnds
	// maps. Submit this grounding map to the virtual links, and see
	// what they've got to say about it.
	if (0 == comp_var_gnds.size())
	{
		if (logger().isFineEnabled())
		{
			logger().fine("Explore one possible combinatoric grounding "
			              "(var_gnds.size = %zu, term_gnds.size = %zu):",
			              var_gnds.size(), term_gnds.size());
			PatternMatchEngine::log_solution(var_gnds, term_gnds);
		}

		// Note, FYI, that if there are no virtual clauses at all,
		// then this loop falls straight-through, and the grounding
		// is reported as a match to the callback.  That is, the
		// virtuals only serve to reject possibilities.
		for (const Handle& virt : virtuals)
		{
			// At this time, we expect all virtual links to be in
			// one of two forms: either EvaluationLink's or
			// GreaterThanLink's. The EvaluationLinks should have
			// the structure
			//
			//   EvaluationLink
			//       GroundedPredicateNode "scm:blah"
			//       ListLink
			//           Arg1Atom
			//           Arg2Atom
			//
			// The GreaterThanLink's should have the "obvious" structure
			//
			//   GreaterThanLink
			//       Arg1Atom
			//       Arg2Atom
			//
			// In either case, one or more VariableNodes should appear
			// in the Arg atoms. So, we ground the args, and pass that
{'fraction_non_alphanumeric': 0.10731319554848967, 'fraction_numerical': 0.008426073131955486, 'mean_word_length': 3.445583038869258, 'pattern_counts': {'":': 0, '<': 55, '<?xml version=': 0, '>': 55, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '335576', 'n_tokens_mistral': 4257, 'n_tokens_neox': 3805, 'n_words': 1254}