Commit e206a390 authored by Akim Demaille's avatar Akim Demaille
Browse files

are-equivalent: bottom-layer implementation for expressions

We do have dyn::are_equivalent for expression, but not on the
templated level.

* vcsn/algos/are-equivalent.hh: Do it.
Use derived-term, instead of standard, as what the case before.
* vcsn/dyn/algos.hh, lib/vcsn/algos/are-equivalent.cc: Adjust.
* tests/python/is-equivalent.py: Check a weighted case.
parent 53b9fe42
{
"metadata": {
"name": "",
"signature": "sha256:75bc55a47cccc7f87607947e6d5a0593cd4aab6f333e905ff4a1bcfb1db650f4"
"signature": "sha256:842fee253f0e59f37fe92befe4006a6c2ddf394b151ec0f2e84b458b67872321"
},
"nbformat": 3,
"nbformat_minor": 0,
......@@ -27,7 +27,7 @@
"- Both weightsets are either $\\mathbb{B}, \\mathbb{Z}$, or a field ($\\mathbb{F}_2, \\mathbb{Q}, \\mathbb{Q}_\\text{mp}, \\mathbb{R}$).\n",
"\n",
"Algorithm:\n",
"- Compute the standard of both expressions, and check whether the automata are equivalent.\n",
"- Compute the derived-term automaton of both expressions, and check whether the automata are equivalent.\n",
"\n",
"See also:\n",
"- [_automaton_.is_equivalent](automaton.is_equivalent.ipynb)"
......@@ -57,7 +57,7 @@
"metadata": {},
"output_type": "display_data",
"text": [
"<IPython.core.display.Javascript at 0x10fc65450>"
"<IPython.core.display.Javascript at 0x11670e450>"
]
}
],
......
......@@ -13,10 +13,11 @@ namespace vcsn
return detail::are_equivalent_registry().call(lhs, rhs);
}
REGISTER_DEFINE(are_equivalent_expression);
bool
are_equivalent(const expression& lhs, const expression& rhs)
{
return are_equivalent(standard(lhs), standard(rhs));
return detail::are_equivalent_expression_registry().call(lhs, rhs);
}
REGISTER_DEFINE(difference);
......
......@@ -160,12 +160,14 @@ def normalize(a):
def CHECK_EQUIV(a1, a2):
'''Check that `a1` and `a2` are equivalent.'''
num = 10
a1 = normalize(a1)
a2 = normalize(a2)
# Cannot compare automata on Zmin.
'''Check that `a1` and `a2` are equivalent. Also works for
expressions.'''
if isinstance(a1, vcsn.automaton):
a1 = normalize(a1)
if isinstance(a2, vcsn.automaton):
a2 = normalize(a2)
# Cannot compute equivalence on Zmin, approximate with shortest.
if str(a1.context()).endswith('zmin') or str(a2.context()).endswith('zmin'):
res = a1.shortest(num) == a2.shortest(num)
else:
......@@ -174,15 +176,16 @@ def CHECK_EQUIV(a1, a2):
if res:
PASS()
else:
FAIL("automata are not equivalent")
rst_file("Left automaton", a1.format('dot'))
rst_file("Right automaton", a2.format('dot'))
FAIL("not equivalent")
rst_file("Left", a1)
rst_file("Right", a2)
s1 = a1.shortest(num).format('list')
s2 = a2.shortest(num).format('list')
rst_file("Left automaton shortest", s1)
rst_file("Right automaton shortest", s2)
rst_file("Left shortest", s1)
rst_file("Right shortest", s2)
rst_diff(s1, s2)
def CHECK_ISOMORPHIC(a1, a2):
"Check that `a1` and `a2` are isomorphic."
if a1.is_isomorphic(a2):
......
......@@ -3,15 +3,18 @@
import vcsn
from test import *
b = vcsn.context('lal_char(ab), b')
# check EXPECTED RAT1 RAT2
# ------------------------
# Check that are-equivalent(RAT1, RAT2) == EXPECTED.
#
# Check that are-equivalent(RAT1, RAT2) == EXPECTED. Because
# is-equivalent on expressions uses is-equivalent on automata under
# the hood, this also checks the case of automata equivalence tests.
def check(exp, r1, r2):
eff = b.expression(r1).is_equivalent(b.expression(r2))
CHECK_EQ(exp, eff)
r1 = ctx.expression(r1)
r2 = ctx.expression(r2)
CHECK_EQ(exp, r1.is_equivalent(r2))
ctx = vcsn.context('lal_char(ab), b')
check(True, '\z', '\z')
check(True, '\e', '\e')
......@@ -25,3 +28,7 @@ check(False, '\z', '\e')
check(False, 'a', 'b')
check(False, 'ab', 'ba')
check(False, 'aa*', 'a*')
ctx = vcsn.context('lal_char(ab), z')
check(True, 'a+b+a', '<2>a+b')
check(True, 'a*+b+a*', '<2>a*+b')
......@@ -4,6 +4,7 @@
#include <vcsn/algos/complement.hh>
#include <vcsn/algos/complete.hh>
#include <vcsn/algos/determinize.hh>
#include <vcsn/algos/derived-term.hh>
#include <vcsn/algos/left-mult.hh>
#include <vcsn/algos/product.hh>
#include <vcsn/algos/reduce.hh>
......@@ -72,6 +73,48 @@ namespace vcsn
}
/*-----------------------------------------.
| are_equivalent(expression, expression). |
`-----------------------------------------*/
/// Check equivalence between two expressions.
template <typename ExpSet1, typename ExpSet2>
auto
are_equivalent(const ExpSet1& rs1,
const typename ExpSet1::value_t r1,
const ExpSet2& rs2,
const typename ExpSet2::value_t r2)
-> bool
{
// We use derived-term because it supports all our operators.
// FIXME: bench to see if standard would not be a better bet in
// the other cases.
return are_equivalent(strip(derived_term(rs1, r1)),
strip(derived_term(rs2, r2)));
}
namespace dyn
{
namespace detail
{
/// Bridge.
template <typename ExpSet1, typename ExpSet2>
bool
are_equivalent_expression(const expression& r1, const expression& r2)
{
const auto& l = r1->as<ExpSet1>();
const auto& r = r2->as<ExpSet2>();
return ::vcsn::are_equivalent(l.expressionset(), l.expression(),
r.expressionset(), r.expression());
}
REGISTER_DECLARE(are_equivalent_expression,
(const expression&, const expression&) -> bool);
}
}
/*-----------------------------------.
| difference(automaton, automaton). |
`-----------------------------------*/
......
......@@ -21,12 +21,12 @@ namespace vcsn
/// An ambiguous word, or raise if there is none.
label ambiguous_word(const automaton& aut);
/// Whether define the same language.
/// Whether compute the same series.
/// \pre The labelsets of lhs and rhs are free.
/// \pre lhs and rhs are Boolean, or on Z, or on a field.
bool are_equivalent(const automaton& lhs, const automaton& rhs);
/// Whether define the same language.
/// Whether denote the same series.
/// \pre The labelsets of lhs and rhs are free.
/// \pre lhs and rhs are Boolean, or on Z, or on a field.
bool are_equivalent(const expression& lhs, const expression& rhs);
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment