OverviewFilesCommitsIssuesDocsReleasesPeers

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

468 lines · cxxtest ref:
/*
 * tests/atoms/AlphaConvertUTest.cxxtest
 *
 * Copyright (C) 2015 Linas Vepstas
 * 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/atoms/base/Atom.h>
#include <opencog/atomspace/AtomSpace.h>
#include <opencog/atoms/scope/ScopeLink.h>
#include <opencog/atoms/pattern/QueryLink.h>

using namespace opencog;

// Test the AlphaConvert.
//
class AlphaConvertUTest :  public CxxTest::TestSuite
{
private:
	AtomSpace _asa;
	AtomSpace _asb;

public:
	AlphaConvertUTest()
	{
		logger().set_print_to_stdout_flag(true);
		logger().set_timestamp_flag(false);
	}

	void setUp() {}

	void tearDown() {}

	void test_untyped_variable();
	void test_variable();
	void test_body();
	void test_atomspace();
	void test_bindlink();
	void test_bindlink_ordered();
	void test_vardecl_free_scope();
	void test_variable_set_scope();
	void test_inner_scope();
	void test_getlink();
};

#define NA _asa.add_node
#define LA _asa.add_link

#define NB _asb.add_node
#define LB _asb.add_link

// Test AlphaConvert over an untyped variable.
void AlphaConvertUTest::test_untyped_variable()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	// Create ScopeLinks.
	Handle hscox =
	LA(SCOPE_LINK,
		NA(VARIABLE_NODE, "$X"),
		LA(AND_LINK, NA(VARIABLE_NODE, "$X")));

	ScopeLinkPtr scox(ScopeLinkCast(hscox));
	TS_ASSERT(scox != nullptr);

	Handle hscoy =
	LB(SCOPE_LINK,
		NB(VARIABLE_NODE, "$Y"),
		LB(AND_LINK, NB(VARIABLE_NODE, "$Y")));

	ScopeLinkPtr scoy(ScopeLinkCast(hscoy));

	// x and y should be equal
	TS_ASSERT(scox->is_equal(hscoy));

	_asb.clear();
	Handle hscoz =
	LB(SCOPE_LINK,
		LB(VARIABLE_SET, NB(VARIABLE_NODE, "$Z")),
		LB(AND_LINK, NB(VARIABLE_NODE, "$Z")));

	// x and z should be equal
	TS_ASSERT(scox->is_equal(hscoz));

	// Constraining something to be "type Atom" is not a constraint.
	_asb.clear();
	Handle hscow =
	LB(SCOPE_LINK,
		LB(TYPED_VARIABLE_LINK,
			NB(VARIABLE_NODE, "$W"), NB(TYPE_NODE, "Atom")),
		LB(AND_LINK, NB(VARIABLE_NODE, "$W")));

	// x and w should be equal
	TS_ASSERT(scox->is_equal(hscow));

	_asb.clear();
	Handle hscot =
	LB(SCOPE_LINK,
		LB(TYPED_VARIABLE_LINK,
			NB(VARIABLE_NODE, "$T"),
			LB(TYPE_CHOICE, NB(TYPE_NODE, "Atom"))),
		LB(AND_LINK, NB(VARIABLE_NODE, "$T")));

	// x and t should be equal
	TS_ASSERT(scox->is_equal(hscot));

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

// Test AlphaConvert over an variable types as ConceptNode.
void AlphaConvertUTest::test_variable()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	// Create ScopeLinks.
	Handle hscox =
	LA(SCOPE_LINK,
		LA(TYPED_VARIABLE_LINK,
			NA(VARIABLE_NODE, "$X"), NA(TYPE_NODE, "ConceptNode")),
		LA(AND_LINK, NA(VARIABLE_NODE, "$X")));

	ScopeLinkPtr scox(ScopeLinkCast(hscox));
	TS_ASSERT(scox != nullptr);

	Handle hscoy =
	LB(SCOPE_LINK,
		NB(VARIABLE_NODE, "$Y"),
		LB(AND_LINK, NB(VARIABLE_NODE, "$Y")));

	ScopeLinkPtr scoy(ScopeLinkCast(hscoy));

	// x and y should be not equal
	TS_ASSERT(not scox->is_equal(hscoy));

	_asb.clear();
	Handle hscoz =
	LB(SCOPE_LINK,
		LB(VARIABLE_LIST, NB(VARIABLE_NODE, "$Z")),
		LB(AND_LINK, NB(VARIABLE_NODE, "$Z")));

	// x and z should be not equal
	TS_ASSERT(not scox->is_equal(hscoz));

	// Constraining should work.
	_asb.clear();
	Handle hscow =
	LB(SCOPE_LINK,
		LB(TYPED_VARIABLE_LINK,
			NB(VARIABLE_NODE, "$W"), NB(TYPE_NODE, "ConceptNode")),
		LB(AND_LINK, NB(VARIABLE_NODE, "$W")));

	// x and w should be equal
	TS_ASSERT(scox->is_equal(hscow));

	_asb.clear();
	Handle hscot =
	LB(SCOPE_LINK,
		LB(TYPED_VARIABLE_LINK,
			NB(VARIABLE_NODE, "$T"),
			LB(TYPE_CHOICE, NB(TYPE_NODE, "Atom"), NB(TYPE_NODE, "ConceptNode"))),
		LB(AND_LINK, NB(VARIABLE_NODE, "$T")));

	// x and t should be equal
	TS_ASSERT(scox->is_equal(hscot));

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

// Test AlphaConvert for differing bodies.
void AlphaConvertUTest::test_body()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	// Create ScopeLinks.
	Handle hscox =
	LA(SCOPE_LINK,
		NA(VARIABLE_NODE, "$X"),
		LA(AND_LINK, NA(VARIABLE_NODE, "$X")));

	ScopeLinkPtr scox(ScopeLinkCast(hscox));
	TS_ASSERT(scox != nullptr);

	Handle hscoy =
	LB(SCOPE_LINK,
		NB(VARIABLE_NODE, "$X"),
		LB(OR_LINK, NB(VARIABLE_NODE, "$X")));

	ScopeLinkPtr scoy(ScopeLinkCast(hscoy));

	// x and y should be NOT equal
	TS_ASSERT(not scox->is_equal(hscoy));

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

// Test alpha conversion in the atomspace.
void AlphaConvertUTest::test_atomspace()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	// Create ScopeLinks.
	Handle hscox =
	LA(SCOPE_LINK,
		NA(VARIABLE_NODE, "$X"),
		LA(AND_LINK, NA(VARIABLE_NODE, "$X")));

	Handle hscoy =
	LA(SCOPE_LINK,
		NA(VARIABLE_NODE, "$Y"),
		LA(AND_LINK, NA(VARIABLE_NODE, "$Y")));

	// x and y should be equal
	TS_ASSERT(hscox == hscoy);

	printf("Expecting same atom: x=%s vs y=%s\n",
		hscox->to_string().c_str(), hscoy->to_string().c_str());

	ScopeLinkPtr scox(ScopeLinkCast(hscox));

	Handle hscoz =
	LA(SCOPE_LINK,
		LA(VARIABLE_SET, NA(VARIABLE_NODE, "$Z")),
		LA(AND_LINK, NA(VARIABLE_NODE, "$Z")));

	// x and z should be equal
	TS_ASSERT(scox->is_equal(hscoz));

	// Constraining something to be "type Atom" is not a constraint.
	_asb.clear();
	Handle hscow =
	LA(SCOPE_LINK,
		LA(TYPED_VARIABLE_LINK,
			NA(VARIABLE_NODE, "$W"), NA(TYPE_NODE, "Atom")),
		LA(AND_LINK, NA(VARIABLE_NODE, "$W")));

	// x and w should be equal
	TS_ASSERT(scox->is_equal(hscow));

	_asb.clear();
	Handle hscot =
	LA(SCOPE_LINK,
		LA(TYPED_VARIABLE_LINK,
			NA(VARIABLE_NODE, "$T"),
			LA(TYPE_CHOICE, NA(TYPE_NODE, "Atom"))),
		LA(AND_LINK, NA(VARIABLE_NODE, "$T")));

	// x and t should be equal
	TS_ASSERT(scox->is_equal(hscot));

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

// Test is_equal for QueryLink.
void AlphaConvertUTest::test_bindlink()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	// Create QueryLink
	Handle hscox =
	LA(QUERY_LINK,
		NA(VARIABLE_NODE, "$X"),
		LA(PRESENT_LINK, NA(VARIABLE_NODE, "$X")),
		LA(AND_LINK, NA(VARIABLE_NODE, "$X")));

	ScopeLinkPtr scox(ScopeLinkCast(hscox));
	TS_ASSERT(scox != nullptr);

	_asb.clear();
	Handle hscoy =
	LB(QUERY_LINK,
		NB(VARIABLE_NODE, "$X"),
		LB(PRESENT_LINK, NB(VARIABLE_NODE, "$X")),
		LB(AND_LINK, NB(VARIABLE_NODE, "$X")));

	// x and y should be equal
	TS_ASSERT(scox->is_equal(hscoy));

	_asb.clear();
	hscoy =
	LB(QUERY_LINK,
		LB(PRESENT_LINK, NB(VARIABLE_NODE, "$X")),
		LB(AND_LINK, NB(VARIABLE_NODE, "$X")));

	// x and y should be equal
	TS_ASSERT(scox->is_equal(hscoy));

	_asb.clear();
	hscoy =
	LB(QUERY_LINK,
		NB(VARIABLE_NODE, "$X"),
		LB(PRESENT_LINK, NA(VARIABLE_NODE, "$X")),
		LB(PRESENT_LINK, NB(VARIABLE_NODE, "$X")));

	// x and y should be NOT equal because their implicants are not
	// alpha-equivalent.
	TS_ASSERT(not scox->is_equal(hscoy));

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

// Test is_equal for QueryLink where the implicand is ordered
void AlphaConvertUTest::test_bindlink_ordered()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

#ifdef RUN_DISABLED_TEST
// This test is disabled, because it was previously passing due to a
// bug in the RuleLink code. The correct fix is not obvious; clearly,
// the PresentLink should be treated as an unorderred link, and therefore
// pass on this lack-of-ordering property to the output ListLink. But
// fixing this is a lot of work, and the primary consumer of this
// algebraic equivalence is PLN, ... which is now obsolete.
	Handle X = NA(VARIABLE_NODE, "$X");
	Handle Y = NA(VARIABLE_NODE, "$Y");

	// Create QueryLink
	Handle hscox =
	LA(QUERY_LINK,
	   LA(PRESENT_LINK, X, Y),
	   LA(LIST_LINK, X, Y));

	ScopeLinkPtr scox(ScopeLinkCast(hscox));
	TS_ASSERT(scox != nullptr);

	_asb.clear();
	Handle hscoy =
	LB(QUERY_LINK,
	   LB(PRESENT_LINK, X, Y),
	   LB(LIST_LINK, Y, X));

	// x and y should be equal
	TS_ASSERT(scox->is_equal(hscoy));

	_asb.clear();
	Handle hscoz =
	LB(QUERY_LINK,
	   LB(VARIABLE_SET, X, Y),
	   LB(PRESENT_LINK, X, Y),
	   LB(LIST_LINK, Y, X));

	// x and z should be equal
	TS_ASSERT(scox->is_equal(hscoz));
#endif // RUN_DISABLED_TEST

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

// Test is_equal for alpha-equivalent scopes with no variable
// declarations.
void AlphaConvertUTest::test_vardecl_free_scope()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	Handle X = NA(VARIABLE_NODE, "$X");
	Handle Y = NA(VARIABLE_NODE, "$Y");

	// Create Scopes with no variable declaration and X and Y swapped.
	Handle hsco1 = LA(SCOPE_LINK, LA(INHERITANCE_LINK, LA(LIST_LINK, X, Y), Y));
	Handle hsco2 = LA(SCOPE_LINK, LA(INHERITANCE_LINK, LA(LIST_LINK, Y, X), X));

	ScopeLinkPtr sco1(ScopeLinkCast(hsco1));
	TS_ASSERT(sco1 != nullptr);

	// hsco1 and hsco2 should be equal as they are alpha-equivalent.
	TS_ASSERT(sco1->is_equal(hsco2));

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

// Test is_equal for scopes with mixture of VariableList, VariableSet
// and no variable declarations.
void AlphaConvertUTest::test_variable_set_scope()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	Handle X = NA(VARIABLE_NODE, "$X");
	Handle Y = NA(VARIABLE_NODE, "$Y");

	Handle sh1 = LA(SCOPE_LINK,
	                LA(VARIABLE_SET, X, Y),
	                LA(LIST_LINK, X, Y));
	Handle sh2 = LA(SCOPE_LINK,
	                LA(VARIABLE_LIST, X, Y),
	                LA(LIST_LINK, X, Y));
	Handle sh3 = LA(SCOPE_LINK,
	                LA(VARIABLE_LIST, Y, X),
	                LA(LIST_LINK, X, Y));
	Handle sh4 = LA(SCOPE_LINK,
	                LA(LIST_LINK, X, Y));

	ScopeLinkPtr sc1 = ScopeLinkCast(sh1);
	ScopeLinkPtr sc2 = ScopeLinkCast(sh2);
	ScopeLinkPtr sc3 = ScopeLinkCast(sh3);
	ScopeLinkPtr sc4 = ScopeLinkCast(sh4);

	TS_ASSERT(not sc1->is_equal(sh2));
	TS_ASSERT(not sc1->is_equal(sh3));
	TS_ASSERT(sc1->is_equal(sh4));
	TS_ASSERT(not sc2->is_equal(sh3));
	TS_ASSERT(not sc2->is_equal(sh4));
	TS_ASSERT(not sc3->is_equal(sh4));

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

// Test alpha equivalence between 2 scopes containing inner scopes
// where variable names are changed both on the outer and inner scopes.
void AlphaConvertUTest::test_inner_scope()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	Handle X = NA(VARIABLE_NODE, "$X");
	Handle Y = NA(VARIABLE_NODE, "$Y");
	Handle Z = NA(VARIABLE_NODE, "$Z");
	Handle W = NA(VARIABLE_NODE, "$W");

	// h1 and h2 are alpha-equivalent, indeed renaming X->Z and Y->W in
	// h1 yields h2.
	Handle h1 = LA(SCOPE_LINK, X, LA(SCOPE_LINK, Y, LA(INHERITANCE_LINK, X, Y)));
	Handle h2 = LA(SCOPE_LINK, Z, LA(SCOPE_LINK, W, LA(INHERITANCE_LINK, Z, W)));

	ScopeLinkPtr sc1 = ScopeLinkCast(h1);
	ScopeLinkPtr sc2 = ScopeLinkCast(h2);

	TS_ASSERT(sc1->is_equal(h2));
}

// Test alpha equivalence between 2 meet links.
void AlphaConvertUTest::test_getlink()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	Handle X = NA(VARIABLE_NODE, "$X");
	Handle Y = NA(VARIABLE_NODE, "$Y");
	Handle A = NA(CONCEPT_NODE, "A");
	Handle B = NA(CONCEPT_NODE, "B");

	// h1 and h2 are alpha-equivalent, indeed renaming X->Y and Y->X in
	// h1 yields h2.
	Handle h1 = LA(MEET_LINK, LA(PRESENT_LINK,
	                            LA(INHERITANCE_LINK, X, A),
	                            LA(INHERITANCE_LINK, Y, B)));
	Handle h2 = LA(MEET_LINK, LA(PRESENT_LINK,
	                            LA(INHERITANCE_LINK, Y, A),
	                            LA(INHERITANCE_LINK, X, B)));

	ScopeLinkPtr sc1 = ScopeLinkCast(h1);
	ScopeLinkPtr sc2 = ScopeLinkCast(h2);

	TS_ASSERT(sc1->is_equal(h2));
}