OverviewFilesCommitsIssuesDocsReleasesPeers

opencog-atomspace / tests / atoms / scope / ScopeLinkUTest.cxxtest

478 lines · cxxtest ref:
/*
 * tests/atoms/ScopeLinkUTest.cxxtest
 *
 * Copyright (C) 2017 OpenCog Foundation
 * All Rights Reserved
 *
 * 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/guile/SchemeEval.h>
#include <opencog/atoms/base/Atom.h>
#include <opencog/atoms/base/Node.h>
#include <opencog/atoms/base/Link.h>
#include <opencog/atomspace/AtomSpace.h>
#include <opencog/atoms/scope/ScopeLink.h>
#include <opencog/atoms/pattern/QueryLink.h>

#include <cxxtest/TestSuite.h>

using namespace opencog;

#define al _asp->add_link
#define an _asp->add_node

// Test ScopeLink.
//
class ScopeLinkUTest :  public CxxTest::TestSuite
{
private:
	AtomSpacePtr _asp;
	SchemeEval* _eval;
	Handle X, Y, Z, S, T, P, Q, CT;

public:
	ScopeLinkUTest() : _asp(createAtomSpace()), _eval(SchemeEval::get_scheme_evaluator(_asp))
	{
		logger().set_print_to_stdout_flag(true);
		logger().set_level(Logger::DEBUG);

		X = an(VARIABLE_NODE, "$X");
		Y = an(VARIABLE_NODE, "$Y");
		Z = an(VARIABLE_NODE, "$Z");
		S = an(VARIABLE_NODE, "$S");
		T = an(VARIABLE_NODE, "$T");
		P = an(PREDICATE_NODE, "P");
		Q = an(PREDICATE_NODE, "Q");
		CT = an(TYPE_NODE, "ConceptNode");

		_eval->eval("(add-to-load-path \"" PROJECT_SOURCE_DIR "\")");
	}

	void setUp() {}

	void tearDown() {}

	void test_content_less();
	void test_content_less_ev();
	void test_content_less_un();
	void test_content_less_uno();
	void test_content_less_st();
	void test_content_less_cpt();

	void test_get_variables_1();
	void test_get_variables_2();
	void test_get_variables_4();
	void test_get_variables_5();
	void test_get_variables_6();
	void test_get_variables_7();

	void test_rand_alpha_conversion();
	void test_names_alpha_conversion();
	void test_vardecl_bindlink_alpha_conversion();
};

void ScopeLinkUTest::test_content_less()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	Handle scX(createLink(SCOPE_LINK, X, X));
	Handle scY(createLink(SCOPE_LINK, Y, Y));

	// scX and scY alpha-equivalent, they should be equal by content,
	// thus neither scX < scY not scY < scX by content.
	content_based_handle_less lt;
	TS_ASSERT(content_eq(scX, scY));
	TS_ASSERT(not lt(scX, scY));
	TS_ASSERT(not lt(scY, scX));

	logger().info("END TEST: %s", __FUNCTION__);
}

void ScopeLinkUTest::test_content_less_ev()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	Handle scX(createLink(SCOPE_LINK, X,
	                   al(EVALUATION_LINK, P, X)));
	Handle scY(createLink(SCOPE_LINK, Y,
	                   al(EVALUATION_LINK, P, Y)));

	// scX and scY alpha-equivalent, they should be equal by content,
	// thus neither scX < scY not scY < scX by content.
	content_based_handle_less lt;
	TS_ASSERT(content_eq(scX, scY));
	TS_ASSERT(not lt(scX, scY));
	TS_ASSERT(not lt(scY, scX));

	logger().info("END TEST: %s", __FUNCTION__);
}

void ScopeLinkUTest::test_content_less_un()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	Handle scX(createLink(SCOPE_LINK,
	                   al(VARIABLE_LIST, X, Y),
	                   al(AND_LINK,
	                      al(EVALUATION_LINK, P, X),
	                      al(EVALUATION_LINK, Q, Y))));
	Handle scY(createLink(SCOPE_LINK,
	                   al(VARIABLE_LIST, Y, X),
	                   al(AND_LINK,
	                      al(EVALUATION_LINK, P, Y),
	                      al(EVALUATION_LINK, Q, X))));

	// scX and scY alpha-equivalent, they should be equal by content,
	// thus neither scX < scY not scY < scX by content.
	content_based_handle_less lt;
	TS_ASSERT(content_eq(scX, scY));
	TS_ASSERT(not lt(scX, scY));
	TS_ASSERT(not lt(scY, scX));

	logger().info("END TEST: %s", __FUNCTION__);
}

void ScopeLinkUTest::test_content_less_uno()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	Handle scX(createLink(SCOPE_LINK,
	                   al(VARIABLE_LIST, X, Y, S, T),
	                   al(AND_LINK,
	                      al(EVALUATION_LINK, P, X),
	                      al(EVALUATION_LINK, P, Y),
	                      al(EVALUATION_LINK, P, S),
	                      al(EVALUATION_LINK, P, T))));
	Handle scY(createLink(SCOPE_LINK,
	                   al(VARIABLE_LIST, S, T, Y, X),
	                   al(AND_LINK,
	                      al(EVALUATION_LINK, P, X),
	                      al(EVALUATION_LINK, P, Y),
	                      al(EVALUATION_LINK, P, S),
	                      al(EVALUATION_LINK, P, T))));

	// scX and scY alpha-equivalent, they should be equal by content,
	// thus neither scX < scY not scY < scX by content.
	content_based_handle_less lt;
	TS_ASSERT(content_eq(scX, scY));
	TS_ASSERT(not lt(scX, scY));
	TS_ASSERT(not lt(scY, scX));

	logger().info("END TEST: %s", __FUNCTION__);
}

void ScopeLinkUTest::test_content_less_st()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	Handle scX(createLink(SCOPE_LINK,
	                   al(VARIABLE_LIST, X, Y),
	                   al(AND_LINK,
	                      al(EVALUATION_LINK, P, X),
	                      al(EVALUATION_LINK, Q, Y))));
	Handle scY(createLink(SCOPE_LINK,
	                   al(VARIABLE_LIST, S, T),
	                   al(AND_LINK,
	                      al(EVALUATION_LINK, P, S),
	                      al(EVALUATION_LINK, Q, T))));

	// scX and scY alpha-equivalent, they should be equal by content,
	// thus neither scX < scY not scY < scX by content.
	content_based_handle_less lt;
	TS_ASSERT(content_eq(scX, scY));
	TS_ASSERT(not lt(scX, scY));
	TS_ASSERT(not lt(scY, scX));

	logger().info("END TEST: %s", __FUNCTION__);
}

void ScopeLinkUTest::test_content_less_cpt()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	Handle A(an(CONCEPT_NODE, "foo"));
	Handle B(an(CONCEPT_NODE, "bar"));

	Handle scX(createLink(SCOPE_LINK,
	                   al(VARIABLE_LIST, X, Y, S, T),
	                   al(AND_LINK,
	                      al(EVALUATION_LINK, P, X),
	                      al(EVALUATION_LINK, Q, Y),
	                      al(EVALUATION_LINK, P, al(LIST_LINK, A, S)),
	                      al(EVALUATION_LINK, Q, al(LIST_LINK, B, T))
	                   )));
	Handle scY(createLink(SCOPE_LINK,
	                   al(VARIABLE_LIST, T, S, Y, X),
	                   al(AND_LINK,
	                      al(EVALUATION_LINK, P, T),
	                      al(EVALUATION_LINK, Q, S),
	                      al(EVALUATION_LINK, P, al(LIST_LINK, A, Y)),
	                      al(EVALUATION_LINK, Q, al(LIST_LINK, B, X))
	                   )));

	// scX and scY alpha-equivalent, they should be equal by content,
	// thus neither scX < scY not scY < scX by content.
	content_based_handle_less lt;
	TS_ASSERT(content_eq(scX, scY));
	TS_ASSERT(not lt(scX, scY));
	TS_ASSERT(not lt(scY, scX));

	logger().info("END TEST: %s", __FUNCTION__);
}

void ScopeLinkUTest::test_get_variables_1()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	Handle sch = al(SCOPE_LINK,
	                X,
	                al(AND_LINK,
	                   al(EVALUATION_LINK, P, X),
	                   al(EVALUATION_LINK, Q, Y)));

	ScopeLinkPtr sc = ScopeLinkCast(sch);

	auto got = sc->get_variables().varset;
	auto exp = HandleSet{X};

	printf("Got %s\n", oc_to_string(got).c_str());
	printf("Excepted %s\n", oc_to_string(exp).c_str());

	TS_ASSERT_EQUALS(got, exp);

	logger().info("END TEST: %s", __FUNCTION__);
}

void ScopeLinkUTest::test_get_variables_2()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	Handle sch = al(SCOPE_LINK,
	                al(TYPED_VARIABLE_LINK, X, CT),
	                an(VARIABLE_NODE, "$P-55ebe96e"),
	                al(EVALUATION_LINK,
	                   an(PREDICATE_NODE, "take"),
	                   an(CONCEPT_NODE, "compound-A")));

	ScopeLinkPtr sc = ScopeLinkCast(sch);

	auto got = sc->get_variables().varset;
	auto exp = HandleSet{X};

	printf("Got %s\n", oc_to_string(got).c_str());
	printf("Excepted %s\n", oc_to_string(exp).c_str());

	TS_ASSERT_EQUALS(got, exp);

	logger().info("END TEST: %s", __FUNCTION__);
}

void ScopeLinkUTest::test_get_variables_4()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	// Test that default variables in scope link are sorted according
	// to canonical order defined in VarScraper
	Handle body = al(INHERITANCE_LINK, X, al(AND_LINK, Y, Z));
	Handle sch = al(SCOPE_LINK, body);

	ScopeLinkPtr sc = ScopeLinkCast(sch);

	auto got = sc->get_variables().varseq;
	Variables expvar;
	expvar.find_variables(body);

	printf("Got %s\n", oc_to_string(got).c_str());
	printf("Excepted %s\n", oc_to_string(expvar.varseq).c_str());

	TS_ASSERT_EQUALS(got, expvar.varseq);

	logger().info("END TEST: %s", __FUNCTION__);
}

void ScopeLinkUTest::test_get_variables_5()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	// Test that variables in scope link provided with VariableSet are
	// sorted according to canonical order defined in VarScraper. We
	// provide a mixture of typed and untyped variables to make sure it
	// covers these cases.
	Handle vardecl = al(VARIABLE_SET,
	                    al(TYPED_VARIABLE_LINK, X, CT),
	                    Y,
	                    al(TYPED_VARIABLE_LINK, Z, CT));
	Handle body = al(INHERITANCE_LINK, X, al(AND_LINK, Y, Z));
	Handle sch = al(SCOPE_LINK, vardecl, body);

	ScopeLinkPtr sc = ScopeLinkCast(sch);

	// Test that variables are in canonical order
	const Variables& result = sc->get_variables();
	Variables expect;
	expect.find_variables(body);

	printf("Got %s\n", oc_to_string(result.varseq).c_str());
	printf("Excepted %s\n", oc_to_string(expect.varseq).c_str());

	TS_ASSERT_EQUALS(result.varseq, expect.varseq);

	// Test that the inferred variable declaration corresponds to the
	// scope one
	Handle re_vardecl = result.get_vardecl();
	Handle sc_vardecl = sc->get_vardecl();

	printf("Got vardecl %s\n", oc_to_string(re_vardecl).c_str());
	printf("Excepted vardecl %s\n", oc_to_string(sc_vardecl).c_str());

	TS_ASSERT(content_eq(re_vardecl, sc_vardecl));

	logger().info("END TEST: %s", __FUNCTION__);
}

void ScopeLinkUTest::test_get_variables_6()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	// Like above but Z is not in the variable declaration thus treated
	// as constant.
	Handle vardecl = al(VARIABLE_SET,
	                    al(TYPED_VARIABLE_LINK, X, CT),
	                    al(TYPED_VARIABLE_LINK, Y, CT));
	Handle body = al(INHERITANCE_LINK, X, al(AND_LINK, Y, Z));
	Handle sch = al(SCOPE_LINK, vardecl, body);

	ScopeLinkPtr sc = ScopeLinkCast(sch);

	HandleSeq result = sc->get_variables().varseq;
	HandleSeq expect{X, Y};

	printf("Got %s\n", oc_to_string(result).c_str());
	printf("Excepted %s\n", oc_to_string(expect).c_str());

	// Test that variables are in canonical order
	TS_ASSERT_EQUALS(result, expect);

	logger().info("END TEST: %s", __FUNCTION__);
}

void ScopeLinkUTest::test_get_variables_7()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	// Given a body with variables from different atomspaces check that
	// the variable list is non redundant.
	AtomSpacePtr alt_as(_asp);
#define alt_al alt_as->add_link
#define alt_an alt_as->add_node
	Handle altU = alt_an(VARIABLE_NODE, "$U");
	Handle altV = alt_an(VARIABLE_NODE, "$V");
	Handle U = an(VARIABLE_NODE, "$U");
	Handle V = an(VARIABLE_NODE, "$V");
	Handle vardecl = al(VARIABLE_SET, U, V);
	Handle ante = al(SIMILARITY_LINK, U, V);
	Handle cnse = alt_al(INHERITANCE_LINK, altU, altV);
	Handle sch = alt_al(RULE_LINK, vardecl, ante, cnse);
#undef alt_al
#undef alt_an

	ScopeLinkPtr sc = ScopeLinkCast(sch);

	HandleSeq result = sc->get_variables().varseq;
	logger().debug() << "Got " << oc_to_string(result);

	// Test that there are no redundant variables (only U and V)
	TS_ASSERT_EQUALS(result.size(), 2);

	logger().info("END TEST: %s", __FUNCTION__);
}

// Test ScopeLink::alpha_conversion with no arguments
void ScopeLinkUTest::test_rand_alpha_conversion()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	// Test simple ScopeLink
	Handle hsc =
	al(SCOPE_LINK,
		an(VARIABLE_NODE, "$X"),
		al(AND_LINK, an(VARIABLE_NODE, "$X")));

	ScopeLinkPtr sc(ScopeLinkCast(hsc));
	Handle scac = sc->alpha_convert();

	std::cout << "sc = " << oc_to_string(Handle(sc));
	std::cout << "scac = " << oc_to_string(scac);

	TS_ASSERT(sc->is_equal(scac));

	logger().info("END TEST: %s", __FUNCTION__);
}

// Test ScopeLink::alpha_conversion with provided variable names
void ScopeLinkUTest::test_names_alpha_conversion()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	// Test simple ScopeLink
	Handle X = an(VARIABLE_NODE, "$X"),
		Y = an(VARIABLE_NODE, "$Y"),
		hsc = al(SCOPE_LINK, X, al(AND_LINK, X));
	Handle expected = createLink(SCOPE_LINK, Y, al(AND_LINK, Y));

	ScopeLinkPtr sc(ScopeLinkCast(hsc));
	Handle result = sc->alpha_convert(HandleMap{{X, Y}});

	std::cout << "result = " << oc_to_string(result);
	std::cout << "expected = " << oc_to_string(expected);

	TS_ASSERT(content_eq(result, expected));

	logger().info("END TEST: %s", __FUNCTION__);
}

// Like test_names_alpha_conversion() but using a QueryLink instead of
// a ScopeLink
void ScopeLinkUTest::test_vardecl_bindlink_alpha_conversion()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	// Test simple QueryLink
	Handle X = an(VARIABLE_NODE, "$X"),
		Y = an(VARIABLE_NODE, "$Y"),
		CT = an(TYPE_NODE, "ConceptNode"),
		Y_vardecl = al(TYPED_VARIABLE_LINK, Y, CT),
		hbl = al(QUERY_LINK, al(AND_LINK, X), al(OR_LINK, X)),
		expected_wo_vardecl = al(QUERY_LINK,
		                         al(AND_LINK, Y), al(OR_LINK, Y)),
		expected = al(QUERY_LINK, Y_vardecl,
		              al(AND_LINK, Y), al(OR_LINK, Y));

	QueryLinkPtr bl(QueryLinkCast(hbl));
	Handle result_wo_vardecl = _asp->add_atom(bl->alpha_convert({Y}));

	std::cout << "result_wo_vardecl = " << oc_to_string(result_wo_vardecl);
	std::cout << "expected_wo_vardecl = " << oc_to_string(expected_wo_vardecl);

	TS_ASSERT_EQUALS(result_wo_vardecl, expected_wo_vardecl);

	logger().info("END TEST: %s", __FUNCTION__);
}

#undef al
#undef an