Source code for FAdo.prax

# -*- coding: utf-8 -*-
"""**Polynomial Random Approximation Algorithms**

.. *Authors:* Rogério Reis & Nelma Moreira

.. *This is part of FAdo project*   https://fado.dcc.fc.up.pt.

.. *Copyright:* 1999-2022 Rogério Reis & Nelma Moreira {rvr,nam}@dcc.fc.up.pt

.. *Contributions by:*
   - Stavros Konstantinidis
   - Mitja Mastnak

.. This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License as published
   by the Free Software Foundation; either version 2 of the License, or
   (at your COption) any later version.

   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 General Public License along
   with this program; if not, write to the Free Software Foundation, Inc.,
   675 Mass Ave, Cambridge, MA 02139, USA."""
from . common import *
from . codes import *
from random import random, randint
from math import ceil


[docs]def minI(a, t, u=None): """ An operator that returns a t-independent language containing L(a) Args: a (FA): the initial automaton t (Transducer): input-altering transducer u (FA | None): universe to consider Returns: NFA: """ tinv = t.inverse() tt = t | tinv if u is None: b = ~(tt.runOnNFA(a)).trim() else: b = (u & ~ (tt.runOnNFA(a))).trim() return (b & ~(tinv.runOnNFA(b))).trim()
[docs]def unive_index(g, aut, prop): """Universality index of a automaton for a given distribution Args: g (GenWordDis): distribution aut (FA): automaton Returns: float: universality index""" n, m = prax_parameters(g) pos = 0.0 b = prop.Aut.runOnNFA(aut) | prop.Aut.inverse().runOnNFA(aut) | aut for _i in range(n): w = next(g) if b.evalWordP(w): pos += 1 return pos/n
[docs]def prax_parameters(g): """Prax parameters for a given experiment""" return (g.n_tries, g.max_length)
[docs]def prax_univ_nfa(g, a, debug=False): """Polynomial Randomized Approximation (PRAX) to NFA universality Args: a (FA): the automaton being tested e (float): admissible error prob_func: probability function alpha (set): alphabet of the language Returns: bool: .. seealso:: S.Konstantinidis, M.Mastnak, N.Moreira, R.Reis. Approximate NFA Universality and Related Problems Motivated by Information Theory, arXiv, 2022. .. versionadded:: 2.0.4""" n,m = prax_parameters(g) for _i in range(n): w = next(g) if not a.evalWordP(w): if debug: print("couterexample of size-> ",len(w), end = " ") return False return True
[docs]def f_dirichlet(n, d=1, t=2.000001): """Dirichlet distribution function Args: n (int): evaluation point d (int | float): displacement t (int | float): Returns: float: .. versionadded:: 2.0.4""" if n >= d: return 1/zeta(t) * ((n + 1 - d) ** (-t)) else: return 0
[docs]def f_laplace(n, d=1, z=0.99): """Laplace distribution function Args: n (int): evaluation point d (int): displacement z (float): a number 9<z<1 Returns: float: Raises: FAdoGeneralError: if z is null""" if z == 0.0: raise FAdoGeneralError("Value of z cannot be null") z = 1/z if n < d: return 0.0 else: return (1-z) * z ** (n-d)
[docs]class GenWordDis(object): """Word generator according to a given distribution funtion (used for sizes), for prax test :ivar list sigma: alphabet :ivar function pf: distribution function :ivar float e: acceptable error :ivar int n_tries: size of the sample :ivar int max_length: maximal size of the words sampled :ivar list dist: comulative probability for each size consedered (up to max_lengtg)""" def __init__(self, f, alf, e): self.sigma = list(alf) self.pf = f e1 = min(e, 1/6) self.e = e1 self.n_tries = ceil(5 / (e1 - 5 * e1 ** 2) ** 2) s, i = 0, 1 while s + e1 * e1 < 1: s += self.pf(i) i += 1 self.max_length = i - 1 foo = 0 self.dist = [] for i in range(1, self.max_length + 1): bar = f(i) self.dist.append(foo + bar) foo += bar def __iter__(self): return self def __next__(self): r = random() sz = self._find(r, 0, len(self.dist) - 1) k = len(self.sigma) return Word([self.sigma[randint(0, k - 1)] for _ in range(sz)]) def _find(self, r, mi, ma): if mi == ma: return mi+1 elif ma-mi == 1: if r <= self.dist[mi]: return mi+1 else: return ma+1 else: i = (ma - mi) // 2 if r <= self.dist[mi + i]: return self._find(r, mi, mi + i) else: return self._find(r, mi + i, ma)