def check_algo(i, o, algo): i = vcsn.automaton(i) o = vcsn.automaton(o) print("using algorithm: ", algo) print("checking proper") # We call sort().strip() everywhere to avoid seeing differences # caused by the different numbering of the states between the # algorithms. CHECK_EQ(o.sort().strip(), i.proper(algo=algo).sort().strip()) # Since we remove only states that _become_ inaccessible, # i.proper(prune=False).accessible() is not the same as i.proper(): # in the former case we also removed the non-accessible states. print("checking proper(prune=False)") CHECK_EQ(o.accessible(), i.proper(prune=False, algo=algo).accessible()) # FIXME: Because proper uses copy, state numbers are changed. # # FIXME: cannot use is_isomorphic because some of our test cases # have unreachable states, which is considered invalid by # is_isomorphic. print("checking idempotence") p = i.proper(algo=algo) if p.is_accessible(): CHECK_ISOMORPHIC(p, p.proper(algo=algo)) else: CHECK_EQ(p.sort().strip(), p.proper(algo=algo).sort().strip())
def check(input, exp): if isinstance(input, str): input = vcsn.automaton(input) if isinstance(exp, str): exp = vcsn.automaton(exp) CHECK_EQ(exp, input.star()) check_algo(input)
def check(i, exp=None): if not exp: exp = i i = vcsn.automaton(i) CHECK_EQ(exp, i.insplit()) # Idempotence. CHECK_ISOMORPHIC(vcsn.automaton(exp), i.insplit().insplit())
def check(i, o): if isinstance(i, str): i = vcsn.automaton(i) CHECK(not i.is_complete()) o = vcsn.automaton(o) CHECK(o.is_complete()) CHECK_EQ(o, i.complete()) # Idempotence. CHECK_EQ(o, o.complete())
def check(input, exp): if isinstance(input, str): input = vcsn.automaton(input) CHECK(not input.is_out_sorted()) aut = input.sort() CHECK_EQ(exp, aut) CHECK(aut.is_out_sorted())
def check_fail_algo(aut, algo): a = vcsn.automaton(aut) try: a.proper(algo=algo) FAIL(r"invalid \\e-cycle not detected") except RuntimeError: PASS()
def null_state(weightset, weight): return vcsn.automaton(''' context = "lal_char, {ws}" $ -> 0 0 -> 1 a 0 -> 2 <{w}>a 1 -> 3 b 2 -> 3 b'''.format(w=weight, ws=weightset))
def check_trim(input, exp): if isinstance(input, str): input = vcsn.automaton(input) aut = input.trim() CHECK_EQ(exp, aut) CHECK(aut.is_trim()) CHECK(aut.is_accessible()) CHECK(aut.is_coaccessible()) CHECK_EQ(exp, input.coaccessible().accessible()) CHECK_EQ(exp, input.accessible().coaccessible())
def null_state_det(weightset, weight): return vcsn.automaton(''' digraph {{ vcsn_context = "letterset<char_letters(ab)>, {ws}" rankdir = LR edge [arrowhead = vee, arrowsize = .6] {{ node [shape = point, width = 0] I0 }} I0 -> 0 [color = DimGray] 0 -> 1 [label = "a", color = DimGray] }}'''.format(ws=ws, w=w))
def update(self, *_): self.updater.abort() self.err.value = '' self.out.clear_output() try: txt = self.text.value a = vcsn.automaton(txt, self.format, strip=False) self.ipython.shell.user_ns[self.name] = \ a.strip() if self.strip else a # There is currently no official documentation on this, # so please check out `ipywidgets.Output`'s docstring. with self.out: a._display(self.mode.value, self.engine.value) except RuntimeError as e: self.err.value = formatError(e)
def check(algo, aut, exp): if isinstance(algo, list): for a in algo: check(a, aut, exp) else: print('checking minimize with algorithm ', algo) CHECK_EQ(exp, aut.minimize(algo)) # Check that repeated minimization still gives the same type of # automaton. We don't want to get partition_automaton of # partition_automaton: one "layer" suffices. CHECK_EQ(exp, aut.minimize(algo).minimize(algo)) # Cominimize. # # Do not work just on the transpose_automaton, to make sure it # works as expected for "forward" automata (which did have one # such bug!). So copy the transposed automaton. t = aut.transpose().automaton(aut.context()) if isinstance(exp, str): exp = vcsn.automaton(exp) CHECK_ISOMORPHIC(exp.transpose(), t.cominimize(algo))
def check(aut, fefsm): 'Check the conversion to and from FSM.' print(here() + ": check:", fefsm) # The reference: text in efsm format. efsm = open(medir + "/" + fefsm).read().strip() # Check output to EFSM. CHECK_EQ(efsm, aut.format('efsm')) # Check print | read | print to EFSM. # # We used to check that the output is exactly what we get when # reading back and printing again. This does not work for automata # with several initial states or a non-one initial weight (which is # approximated below as !is_standard), as then we show pre as the # real initial state. The pre state is displayed as the state # number immediately after the highest state number, and when read # and printed back by OpenFST, it is renumbered as 0. # # So (read | print) is not the identity. aut2 = vcsn.automaton(efsm, 'efsm') if aut.is_standard(): print(here(), 'case standard') CHECK_EQ(aut, aut2) CHECK_EQ(efsm, aut2.format('efsm')) else: print(here(), 'case non standard') CHECK_EQUIV(aut, normalize(aut2)) # Check that OpenFST accepts and reproduces our EFSM files. if have_ofst: if aut.is_standard(): print(here(), 'fstcat: eq') CHECK_EQ(aut, aut.fstcat()) else: print(here(), 'fstcat: equiv') CHECK_EQUIV(aut, normalize(aut.fstcat())) else: SKIP('OpenFST is missing')
def _aut_of_d3(self): '''Conversion from d3 to an automaton, via "daut".''' self.error.value = '' ctx = 'context = {:s}\n'.format(self.context) trans = self._widget.transitions aut = '' def get_state(obj): '''Convert whatever the given state is into a daut state.''' # Things that are not constant: # - PRE and POST are decimals or '$' # - states are strings or dicts if isinstance(obj, dict): state = str(obj['id']) elif isinstance(obj, str): state = obj try: # State ID is a valid integer number int(state) return state except ValueError: return '$' for t in trans: src = get_state(t['source']) dst = get_state(t['target']) aut += "{} -> {} {}\n".format(src, dst, t['label']) res = ctx + aut try: self.error.value = str(trans) return vcsn.automaton(res, 'daut') except RuntimeError as e: ts = ['{} -> {}'.format(t['source']['id'], t['target']['id']) for t in trans if isinstance(t['source'], dict) and isinstance(t['target'], dict)] self.error.value = vcsn.ipython.formatError(str(e) + '\n#####\n' + res + '\n#####\n' + '\n'.join(ts))
def automaton(self, line, cell=None): '''An automaton editor.''' args = parse_argstring(self.automaton, line) if not args.var.isidentifier(): raise NameError('`{}` is not a valid variable name'.format( args.var)) if cell is None: # Line magic. if args.format == 'auto': args.format = 'daut' if args.format == 'gui': a = d3Widget.VcsnD3DataFrame(self, args.var) a.show() else: AutomatonText(self, args.var, args.format, layout=args.layout, strip=args.strip) else: # Cell magic. a = vcsn.automaton(cell, format=args.format, strip=args.strip) self.shell.user_ns[args.var] = a display(a)
## ---------- ## # We do not bind the compare functions, which are too precise # currently: swapping two transitions make the automata different. def check_lt(a1, a2): CHECK_EQ(True, a1.compare(a2) < 0) CHECK_EQ(True, a2.compare(a1) > 0) CHECK_EQ(0, a1.compare(a1)) CHECK_EQ(0, a2.compare(a2)) ctx = vcsn.context('lal_char, q') # First, comparison on source state numbers. a1 = vcsn.automaton('''context = lal, z 0 -> 0 a 0 -> 1 a ''') a2 = vcsn.automaton('''context = lal, z 0 -> 0 a 1 -> 0 a ''') check_lt(a1, a2) # Second, comparison on labels. a1 = vcsn.automaton('''context = lal, z $ -> 0 0 -> 0 a 0 -> $''') a2 = vcsn.automaton('''context = lal, z $ -> 0 0 -> 0 b
def datafile(f): return '{datadir}/sms2fr/{file}.efsm'.format( datadir=vcsn.config('configuration.datadir'), file=f) # Handle arguments. def create_args(): parser = argparse.ArgumentParser() parser.add_argument('-s', '--syntactic', type=str, default=datafile('syntactic')) parser.add_argument('-g', '--graphemic', type=str, default=datafile('graphemic')) return parser.parse_args() args = create_args() # Read the graphemic automaton. grap = vcsn.automaton(filename=args.graphemic) # Read the syntactic automaton. synt = vcsn.automaton(filename=args.syntactic).partial_identity() for line in sys.stdin: trad = sms_to_fr(line.replace('\n', ''), grap, synt) eff = re.match('<(.*)>(.*)', trad).group(2) print(eff[3:-3].replace('#', ' '))
def meaut(fn, ext=None): '''The automaton stored in the test's file `fn.ext`, where `ext` is possibly empty.''' return vcsn.automaton(filename=mefile(fn, ext))
def xfail(algo, aut): res = '' try: res = aut.minimize(algo) except RuntimeError: PASS() else: FAIL('did not raise an exception', str(res)) ## Simple minimization test. The example comes from the "Théorie des ## langages" lecture notes by François Yvon & Akim Demaille. ## Automaton 4.23 at page 59, as of revision a0761d6. a = meaut('redundant.gv') exp = metext('redundant.exp.gv') check('brzozowski', a, vcsn.automaton(exp)) check(algos, a, exp) ## An automaton equal to redundant.exp, with one transition removed. a = meaut('incomplete-non-trim.gv') #xfail('brzozowski', a) xfail('moore', a) xfail('signature', a) xfail('weighted', a) ## An automaton equal to redundant.exp, with no initial states. It ## must be minimized into an empty automaton. a = meaut('no-initial-states.gv') z = metext('no-initial-states.exp.gv') check('brzozowski', a, z) xfail('moore', a)
import vcsn from test import * def check(i, o): i1 = i.push_weights() CHECK_EQ(o, i1) # Make sure the expected result is consistant. CHECK_EQUIV(o, i1) # q i = vcsn.automaton('''digraph { vcsn_context = "lal_char(abc), q" I0 -> 0 0 -> 1 [label = "<2>a"] 1 -> 2 [label = "<3>b"] 2 -> 3 [label = "<5>c"] 3 -> 4 [label = "<2>b"] 1 -> 3 [label = "<8>c"] 4 -> F4 }''') o = vcsn.automaton('''digraph { vcsn_context = "lal_char(abc), q" I0 -> 0 [label = "<92>"] 0 -> 1 [label = "a"] 1 -> 2 [label = "<15/23>b"] 1 -> 3 [label = "<8/23>c"] 2 -> 3 [label = "c"] 3 -> 4 [label = "b"] 4 -> F4 }''') check(i, o)
p = aut.proper().sort().strip() CHECK_EQUIV(p, pfst) if have_ofst: # Conjunction: check that OpenFST and Vcsn understand the weights # the same way. We have zmin and log in common. for f in [ vcsn.datadir + '/lal_char_zmin/minblocka.gv', vcsn.datadir + '/lal_char_zmin/slowgrow.gv', medir + '/lal-char-log.gv' ]: print("Conjunction:", f) # # a & 2 by Vcsn. a = vcsn.automaton(filename=f) a2_vcsn = a & 2 # c1 & c1 by OpenFST. a2_ofst = a.fstconjunction(a) CHECK_EQ(a2_vcsn, a2_ofst) # Make sure determinizations agree. This automaton, determinized, # has weights on the final states only, which exercises a bug we # once had. print("Determinize") zmin = vcsn.context('lal_char(ab), zmin') a = zmin.expression('[ab]*a(<2>[ab])').automaton() d_vcsn = a.determinize().strip() d_ofst = a.fstdeterminize()
#! /usr/bin/env python import vcsn from test import * ## ----- ## ## LAL. ## ## ----- ## a = vcsn.automaton('''digraph { vcsn_context = "lat<lal_char(abc), lal_char(xyz)>, z" I0 -> 0 0 -> 1 [label = "<2>(a, x)"] 1 -> 2 [label = "<3>(b, y)"] 2 -> F2 }''') CHECK(a.is_functional()) a = vcsn.automaton(r'''digraph { vcsn_context = "lat<lal_char(abc),lal_char(xyz)>, b" I0 -> 0 0 -> 1 [label = "(a, x)"] 0 -> 2 [label = "(a, x)"] 1 -> 3 [label = "(b, y)"] 2 -> 3 [label = "(b, y)"] 3 -> F3 }''') CHECK(a.is_functional()) a = vcsn.automaton('''digraph { vcsn_context = "lat<lal_char(abc),lal_char(xyz)>, b"
#! /usr/bin/env python import vcsn from test import * # Not deterministic, yet not ambiguous. a = vcsn.automaton(''' digraph { vcsn_context="lal_char(ab), b" I -> 0 0 -> 1 [label = "a"] 0 -> 2 [label = "a"] 1 -> F } ''') CHECK(not a.is_ambiguous()) XFAIL(lambda: a.ambiguous_word(), "automaton is unambiguous") CHECK(not a.is_deterministic()) # Not deterministic, and ambiguous. a = vcsn.automaton(''' digraph { vcsn_context="lal_char(ab), b" I -> 0 0 -> 1 [label = "a"] 0 -> 2 [label = "a"] 1 -> F 2 -> F }
#! /usr/bin/env python import vcsn from test import * ## ----- ## ## LAL. ## ## ----- ## a = vcsn.automaton('''digraph { vcsn_context = "lat<lal_char(abc), lal_char>, z" I0 -> 0 0 -> 1 [label = "(a, a)"] 1 -> 2 [label = "(b, b)"] 2 -> F2 }''') CHECK(a.is_partial_identity()) a = vcsn.automaton('''digraph { vcsn_context = "lat<lal_char(abc), lal_char>, z" I0 -> 0 0 -> 1 [label = "(a, a)"] 1 -> 2 [label = "(b, c)"] 2 -> F2 }''') CHECK(not a.is_partial_identity()) ## ----- ## ## LAW. ## ## ----- ##
# LAN x LAN ctx = vcsn.context('lat<lan_char(a), lan_char(x)>, q') check_shortest( r'(\e|x + a|\e)*', 9, r'\e|\e + \e|x + a|\e + <2>a|x + \e|xx + <3>a|xx + aa|\e + <3>aa|x + <6>aa|xx' ) check_enumerate( r'(\e|x + a|\e)*', 2, r'\e|\e + \e|x + a|\e + <2>a|x + \e|xx + <3>a|xx + aa|\e + <3>aa|x + <6>aa|xx' ) aut = vcsn.automaton(''' context = "letterset<char_letters(a)>, q" $ -> 0 0 -> 1 <2>a 0 -> 2 <-1>a 0 -> 3 <-1>a 1 -> $ 2 -> $ 3 -> $ ''') check_one(aut, 1, '\z') aut = vcsn.automaton(''' context = "letterset<char_letters(ab)>, q" $ -> 0 0 -> 1 <2>a 0 -> 2 <-1>a 0 -> 3 <-1>a 0 -> 4 b 1 -> $ 2 -> $
result = vcsn.automaton(''' digraph { vcsn_context = "lal_char(abc), b" rankdir = LR { node [shape = point, width = 0] I0 F0 F1 F2 F3 F4 } { node [shape = circle] 0 1 2 3 4 } I0 -> 0 0 -> F0 0 -> 1 [label = "a"] 0 -> 2 [label = "b"] 0 -> 3 [label = "b"] 0 -> 4 [label = "c"] 1 -> F1 1 -> 1 [label = "a"] 1 -> 2 [label = "b"] 1 -> 3 [label = "b"] 1 -> 4 [label = "c"] 2 -> F2 2 -> 1 [label = "a"] 2 -> 2 [label = "b"] 2 -> 3 [label = "b"] 2 -> 4 [label = "c"] 3 -> F3 3 -> 3 [label = "b"] 3 -> 4 [label = "c"] 4 -> F4 4 -> 3 [label = "b"] 4 -> 4 [label = "c"] } ''')
def check(exp, aut): CHECK_EQ(exp, vcsn.automaton(aut).is_proper())
CHECK_EQ(meaut('binary^0.gv'), binary & 0) # power 1. CHECK_EQ(meaut('binary^1.gv'), binary & 1) # power 4. binary4 = binary & 4 # 0^4 = 0. CHECK_EQ(z.weight('0'), binary4('0')) # 1^4 = 1. CHECK_EQ(z.weight('1'), binary4('1')) # 4^4 = 256. CHECK_EQ(z.weight('256'), binary4('100')) # Power 5. binary5 = binary & 5 CHECK_EQ(z.weight('32'), binary5('10')) #run 0 '' -vcsn power -o binary^5.gv -f $binary_gv 5 #run 0 32 -vcsn evaluate -f binary^5.gv 10 # Power 7. binary7 = binary & 7 CHECK_EQ(z.weight('128'), binary7('10')) # Check that only the accessible part is kept using binary.gv, # modified with an inaccessible part. inacc = binary + vcsn.automaton('''context = lal, z 0 -> 1 0''') for i in [0, 1, 4, 7]: CHECK_EQ(binary & i, (inacc & i).__value__())
I0 -> 0 0 -> F0 [label = "<22>"] 0 -> 0 [label = "<10>a|x, <5>b|y"] }''') ## ------------------------------- ## ## Tools Sec. 3.3.1, Fig. 3.14. ## ## ------------------------------- ## a = vcsn.automaton(''' digraph { vcsn_context = "lal_char(abc), z" I -> 0 1 -> F 0 -> 0 [label = "a, b"] 0 -> 1 [label = "b"] 0 -> 2 [label = "<2>b"] 2 -> 2 [label = "<2>a, <2>b"] 2 -> 1 [label = "<2>b"] 1 -> 1 [label = "<4>a, <4>b"] } ''') check_reduce( a, '''digraph { vcsn_context = "letterset<char_letters(abc)>, z" rankdir = LR edge [arrowhead = vee, arrowsize = .6] { node [shape = point, width = 0] I0
#! /usr/bin/env python import vcsn from test import * # # Simple test, one spontaneous transition # a \e a # -> 0 -> 1 -> 2 -> 3 -> # aut = vcsn.automaton(r''' context = "lan_char(a), b" $ -> 0 0 -> 1 a 1 -> 2 \e 2 -> 3 a 3 -> $ ''') lazy = aut.proper(lazy=True) CHECK_EQ( '''digraph { vcsn_context = "letterset<char_letters(a)>, b" rankdir = LR edge [arrowhead = vee, arrowsize = .6] { node [shape = point, width = 0] I0 F3
# ------------------ # Check that is-normalized(INPUT) = EXPECT. def check(expect, i): CHECK_EQ(expect, i.is_normalized()) # Check normalize CHECK_EQ(i.standard().costandard(), i.normalize()) # A simple, normalized, non-Thompson, automaton. check( True, vcsn.automaton(''' digraph { vcsn_context = "lal_char(ab), z" I0 -> 0 0 -> 1 [label = "a,b"] 1 -> F1 } ''')) # No initials check( False, vcsn.automaton(''' digraph { vcsn_context = "lal_char(ab), z" 0 -> 1 [label = "a,b"] 1 -> F }