OverviewFilesCommitsIssuesDocsReleasesPeers

opencog-generate / opencog / generate / GenerateCallback.h

191 lines · c ref:
/*
 * opencog/generate/GenerateCallback.h
 *
 * Copyright (C) 2020 Linas Vepstas <linasvepstas@gmail.com>
 *
 * 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.
 */

#ifndef _OPENCOG_GENERATE_CALLBACK_H
#define _OPENCOG_GENERATE_CALLBACK_H

#include <opencog/atomspace/AtomSpace.h>
#include <opencog/generate/Odometer.h>

namespace opencog
{
/** \addtogroup grp_generate
 *  @{
 */

/// Aggregation selection callbacks. As an assembly is being created,
/// that assembly will have unconnected, open connectors on it.
/// Aggregation proceeds by attaching sections ("puzzle pieces") to
/// each open connector, until there are none left. But what to connect
/// to what? The master aggregation algorithm defers that choice to this
/// callback API.  Different algos can offer up different connections to
/// try out. The master aggregation algo manages the overall process of
/// connecting things up; the callbacks suggest what to connect next.
///
/// The master aggregation algo is stack-based, and potentially
/// combinatoric-explosive, as each the current set of open connectors
/// depends on the history of what was previously attached. Because
/// the algo is breadt-first (see other descriptions) two stacks are
/// maintained: one for each "row" (odometer) and one for each
/// odometer-wheel.
///
class GenerateCallback
{
protected:
	AtomSpace* _as;
public:
	GenerateCallback(AtomSpace* as) : _as(as) {}
	virtual ~GenerateCallback() {}

	/// Callbacks are allowed to keep dynamical state. Callbacks are
	/// allowed to be reused for multiple generations. This gives the
	/// callback the opportunity to clear dynamical state. In addition,
	/// this provides a scratch space where temporary results can be
	/// stored for the duration.
	virtual void clear(AtomSpace*) = 0;

	/// Declare a set of initial starting (nucleation) points.  The
	/// generated graph will grow outwards from these points.
	virtual void root_set(const HandleSet& points) = 0;

	/// Iterator, returning the next untried initial starting (nucleation)
	/// sections. These are meant to be a set of sections, containing
	/// the nucleation points, from which the graph generation can grow.
	/// The nucleation points were previously declated by `root_set()`.
	/// Returns the emtpy set, when there is no more to be done.
	virtual HandleSet next_root(void) = 0;

	/// Given a connector, return a set of matching connectors
	/// that this particular connector could connect to. This
	/// set may be empty, or may contain more than one match.
	virtual HandleSeq joints(const Handle&) = 0;

	/// Given an existing connected section `fm_sect` and a connector
	/// `fm_con` on that section, as well as a mating `to_con`, return
	/// a section that could be attached. This allows the callback to
	/// chose sections in such a way that the highest-priority or most
	/// preferable sections are mated first.  Return null handle to
	/// discontinue mating.
	///
	/// The `fm_con` is the connector located at `offset` in the
	/// `fm_sect`.
	///
	/// This should be implemented so that it behaves like a 'future'
	/// or a 'promise', so that, when called, it returns the next
	/// section from a (virtual) list of eligible sections.
	virtual Handle select(const OdoFrame&,
	                      const Handle& fm_sect, size_t offset,
	                      const Handle& to_con) = 0;

	/// Create a link from connector `fm_con` to connector `to_con`,
	/// which will connect `fm_pnt` to `to_pnt`.
	virtual Handle make_link(const Handle& fm_con, const Handle& to_con,
	                         const Handle& fm_pnt, const Handle& to_pnt) = 0;

	/// Return a count of the number of links between `fm_sect` and
	/// `to_sect` of type `link_type`. This should return zero if these
	/// two sections are not nearest neighbors.  Return one, if there
	/// is exactly one link. Return two or more, if there are "parallel"
	/// links in place -- if the two sections are multiply-connected.
	virtual size_t num_links(const Handle& fm_sect, const Handle& to_sect,
	                         const Handle& link_type) = 0;

	virtual void push_frame(const OdoFrame&) {}
	virtual void pop_frame(const OdoFrame&) {}

	virtual void push_odometer(const Odometer&) {}
	virtual void pop_odometer(const Odometer&) {}

	/// Called before taking a step of the odometer.
	/// Return `true` to take the step, else false.
	/// Returning false will abort the current odometer.
	/// Traversal will resume at an earlier level.
	///
	/// The default below allows infinite recursion.
	virtual bool step(const OdoFrame&) { return true; }

	/// Called when a solution is found. A solution is a linkage,
	/// with no open connectors.
	virtual void solution(const OdoFrame&) = 0;

	/// Return a SetLink with all of the solutions that were
	/// reported via the above callback. Perform any other AtomSpace
	/// maintanence pertaining to reporting the solutions.
	virtual Handle get_solutions(void) = 0;

	// ---------------------------------------------------------------
	/// Generic Parameters
	/// These are parameters that all callback systems might reasonably
	/// want to consult, when determining behavior.
	// (Should these be moved to thier own class???)

	/// Maximum number of solutions to accept. Search is halted after
	/// this number is reached.
	size_t max_solutions = -1;

	/// Allow connectors on an open section to connect back onto
	/// themselves (if the other mating rules allow the two connectors
	/// to connect).
	bool allow_self_connections = false;

	/// The maximum number of any kind of link allowed between a pair
	/// of sections. By default, it is one, as "most" "typical" graphs
	/// make sense when only one edge connects a pair of vertexes.
	size_t pair_any_links = 1;

	/// The maximum number of each specific type of link allowed between
	/// a pair of sections. For example, if this is set to two, then two
	/// edges of different types can connect the same pair of points.
	/// This is ignored if `pair_any_links` (above) is 1.
	size_t pair_typed_links = 1;

	/// Maximum size of the generated network. Exploration of networks
	/// larger than this will not be attempted.
	size_t max_network_size = -1;

	/// Maximum depth to explore from the starting point. This is
	/// counted in terms of the maximum depth of the stack of
	/// odometers. This is maximum diameter of the network, as measured
	/// from the starting point.
	size_t max_depth = -1;

	/// Maximum number of odometer steps to take. This needs to be
	/// capped, as infinite loops are possible, especially with the
	/// random selector, which, with approrpaite weightings, can
	/// explosively create infinite networks. (Its potentially
	/// possible to determine, in advance, if a given weighting scheme
	/// will create infinite trees, mon average. But this does not seem
	/// like a high priority task, right now.
	/// (2016 vintage CPU run at approx 1.2K steps/second).
	size_t max_steps = 25101;

	/// A location to which all point instances will be anchored.
	/// Thus, all points can be found by following the MemberLink
	/// from this anchor point.
	Handle point_set = Handle::UNDEFINED;
};


/** @}*/
}  // namespace opencog

#endif // _OPENCOG_GENERATE_CALLBACK_H