dees/pfa.cpp
2016-09-24 14:08:58 +02:00

714 lines
18 KiB
C++

/***************************************************************************
pfa.cpp
-------------------
begin : Mon 9 Dec 2002
copyright : (C) 2002 by Yann Esposito
email : esposito@cmi.univ-mrs.fr
***************************************************************************/
/***************************************************************************
* *
* 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 option) any later version. *
* *
***************************************************************************/
/*
Pour les notes, voir le fichier "pfa.h".
See "pfa.h" file to view notes.
*/
#include "pfa.H"
//#include <list>
#include <assert.h>
// renvoi vrai si le MA est un PFA
RESULT
PFA::rend_PFA (void)
{
// on emmonde
MA::emmonde();
// renormalisation of all parameters
renormalise();
return VAL (0);
}
RESULT
PFA::becomeRandom (const int nb_etats, const int nb_lettres,
const int num_graphe, const float densite,
const float prob_init, const float prob_term,
const float min_trans, const float max_trans)
{
// on cree un SPFA
SPFA::becomeRandom (nb_etats, nb_lettres,
num_graphe, densite,
prob_init, prob_term,
min_trans, max_trans);
// puis on l'emmonde et on le renormalise.
return rend_PFA ();
}
// Cree un MA aleatoire avec un nombre maximal d'aretes
RESULT PFA::becomeRandomMax (const int nb_etats, const int nb_lettres,
const int num_graphe, const int nb_succ,
const int nb_init, const int nb_term,
const float min_trans, const float max_trans)
{
SPFA::becomeRandomMax(nb_etats, nb_lettres, num_graphe, nb_succ, nb_init, nb_term,min_trans,max_trans);
return rend_PFA();
}
RESULT
PFA::becomeRandomPrefix (const int nb_etats, const int nb_lettres,
const int num_graphe, const float densite, const float prob_init,
const float prob_term, const float min_trans,
const float max_trans)
{
// On gÈnËre un MA alÈatoire avec seulement des arÍtes positives
SPFA::becomeRandomPrefix (nb_etats, nb_lettres,
num_graphe, densite,
prob_init, prob_term,
min_trans, max_trans);
// on rend un SPFA
return rend_PFA ();
}
// Arbre prefixe
RESULT PFA::becomePrefixTree(Sample &S)
{
bool ispref = S.isprefixiel();
if (!ispref)
{
S.prefixialise();
}
vide(); // on vide la structure du PFA
Sample::const_iterator w;
Lettre a;
Word v;
Sigma=S.alphabet();
alph=S.dictionnaire();
map< Word, State > R;
w = S.begin();
R[w->first]=addNewState(1,0);
// on commence apres epsilon d'ou le ++w au debut du for
for (++w ; w != S.end() ; ++w)
{
v=w->first;
R[v]=addNewState(0,0);
a=*(v.rbegin());
v.erase(--v.end());
addTransition(R[v], a , R[w->first],(float)S[w->first]/(float)S[v]);
}
// on met ‡ jour iota et tau
StateSet::const_iterator e;
Graph::const_iterator q;
LettreSFunc::const_iterator x;
SFunc::const_iterator r;
float sum;
for (e=Q.begin() ; e != Q.end() ; ++e)
{
sum = 0;
q=phi.find(*e);
if (q != phi.end())
{
for (x=q->second.begin() ; x != q->second.end() ; ++x)
{
for (r=x->second.begin() ; r != x->second.end() ; ++r)
{
sum += r->second;
}
}
}
tau[*e]=1-sum;
}
if (!ispref)
{
S.deprefixialise();
}
return VAL(0);
}
// Lissage
RESULT PFA::lisse (int mode, float delta)
{
if (mode == 0)
{ // Mode sale !!!
SFunc::iterator q;
Alphabet::const_iterator a;
float val;
State s; // le nouvel etat
if (iota.empty ())
{
s = addNewState(1,delta);
}
else
{
q = iota.begin ();
val = q->second * delta;
q->second = q->second * (1 - delta);
s = addNewState (val, delta);
}
val = 1 - delta;
for (a = Sigma.begin (); a != Sigma.end (); ++a)
addTransition (s, *a, s, val / (Sigma.size ()));
return VAL(0);
}
else if (mode == 1)
{
cerr << "PFA::lisse(), mode " << mode << " non pris en compte"
<< endl;
return ERR (1);
}
else
{
cerr << "PFA::lisse(), mode" << mode << " non pris en compte" << endl;
return ERR (0);
}
}
// Vraissemblance
float PFA::Likelihood(const Sample &S)
{
if (S.isprefixiel()) {
cerr << "PFA::Likelihood(const Sample &S) : S is prefixial !" << endl;
return -1;
}
Sample::const_iterator w;
float result=0;
// cout.precision(19);
for (w=S.begin() ; w != S.end() ; ++w)
{
if (isnan(plog(w->first))) {
cerr << "ERREUR mot : " << w->first << endl;
}
else {
result += plog(w->first) * w->second;
}
}
return result;
}
// genere un mot ‡ l'aide du PFA.
RESULT
PFA::genere_mot (Word & w, const bool safe)
{
if (PFA_SAFE)
{
if (safe)
assert (isPFA ());
}
State jeton;
float alea;
float sum;
SFunc::const_iterator q;
LettreSFunc::const_iterator a;
// w = mot vide
w.clear ();
// choix de l'Ètat initial q
// on prend un nombre de [0,1[
alea = ((float) rand ()) / INT_MAX;
// on prend le premier etat qui fait dÈpasser le seuil
q = iota.begin ();
sum = q->second;
while (sum < alea)
{
sum += (++q)->second;
}
jeton = q->first; // on a fait notre choix
// Fin du choix : on commence dans l'Ètat jeton
// on genere le mot
// alea prend une valeur au hasard de [0,1[, puis tant que l'on ne s'arrete pas
// i.e. tant que alea > tau(jeton),
alea = ((float) rand ()) / INT_MAX;
while (alea >
(sum =
(tau.find (jeton) !=
tau.end ())? (tau.find (jeton)->second) : 0))
{
// on choisi une transition ‡ emprunter
a = (phi.find (jeton)->second).begin (); //On n'a pas besoin de faire un test sur le find !
while (sum < alea)
{
q = a->second.begin ();
while ((q != a->second.end ()) && (sum < alea))
{
if ((sum += q->second) < alea)
q++;
}
if (sum < alea)
a++;
}
// et on l'emprunte
jeton = q->first;
// on rajoute la lettre gÈnÈrÈe ‡ w
w += a->first;
//w.push_back (a->first);
// on est dans l'Ètat "jeton" et on a ajoute la lettre "a->first"
//puis on recommence
alea = ((float) rand ()) / INT_MAX;
}
return VAL (0);
}
// genere un Èchantillon de mots
RESULT
PFA::genere_echantillon (int taille, Sample & S,
const int num_echantillon)
{
if (isPFA()) {
int i;
Word w;
if (num_echantillon != 0)
{
srand (num_echantillon);
}
S = Sample (Sigma, alph, false);
for (i = 0; i < taille; i++)
{
genere_mot (w, false);
S.insert (w);
}
return VAL (0);
}
else {
return MA::genere_echantillon(taille, S, num_echantillon);
}
}
// genere un Èchantillon de mots : l'echantillon reste identique si lot est identique
RESULT PFA::genere_echantillon (const int taille,
Sample & S,
const char *Filename,
const int num_echantillon)
{
if (PFA_DEBUG)
{
if (Filename == NULL)
{
cerr << " PFA::echantillon(int taille, char *Filename) : Filename = NULL !!!" << endl;
}
}
genere_echantillon (taille, S, num_echantillon);
return S.save (Filename);
}
// ---------- DISTANCES ---------
// renvoie la perplexité du PFA par rapport à l'echantillon S
double
PFA::perplexite (const Sample & S) const
{
double res, sum;
Sample::const_iterator w;
if (isPFA()) {
res = sum = 0;
for (w = S.begin (); w != S.end (); S.next (w))
{
res += plog (w->first) * w->second;
sum += w->second;
}
}
else {
res = sum = 0;
for (w = S.begin (); w != S.end (); S.next (w))
{
res += log (p(w->first)) * w->second;
sum += w->second;
}
}
return -res / sum;
}
// renvoie la perplexitÈ du PFA par rappor ‡ l'echantillon S
double
PFA::divKL (PFA &B, const Sample & S) const
{
double res, res2, sum;
Sample::const_iterator w;
res = res2 = sum = 0;
for (w = S.begin (); w != S.end (); S.next (w))
{
res += plog (w->first) * w->second;
res2 += B.plog(w->first) * w->second;
sum += w->second;
}
return (res2 - res) / sum;
}
// renvoie la norme 2 entre ce PFA et le PTA correspondant ‡ S
double
PFA::d_2(const Sample &S) const {
Sample::const_iterator w;
double res, sum;
double pA, pB;
double taille=double(S.size());
res = sum = 0;
for (w = S.begin (); w != S.end (); w++)
{
if (w->second >= S.seuil)
{
pA = p (w->first);
pB = double(w->second)/taille;
res += (pA - pB) * (pA - pB) * pA;
sum += pA;
}
}
return sqrt (res / sum);
}
// renvoie la norme 2 entre ce PFA et un autre
double
PFA::d_2 (const PFA & B, const Sample & S) const
{
Sample::const_iterator w;
double res, sum;
double pA, pB;
res = sum = 0;
for (w = S.begin (); w != S.end (); w++)
{
if (w->second >= S.seuil)
{
pA = p (w->first);
pB = B.p (w->first);
res += (pA - pB) * (pA - pB) * pA;
sum += pA;
}
}
return sqrt (res / sum);
}
// renvoie la norme L1 entre ce PFA et un autre
double
PFA::L_1 (const PFA & B, const Sample & S) const
{
Sample::const_iterator w;
double res, sum;
double pA, pB;
res = sum = 0;
for (w = S.begin (); w != S.end (); w++)
{
if (w->second >= S.seuil)
{
pA = p (w->first);
pB = B.p (w->first);
res += (pA - pB >= 0) ? (pA - pB) * pA : (pB - pA) * pA; // |pA(w) - pB(w)|*pA(w)
sum += pA;
}
}
return sqrt (res / sum);
}
// renvoie la norme 2 entre ce PFA et un autre
double
PFA::d_2nlog (const PFA & B, const Sample & S) const
{
Sample::const_iterator w;
double res, sum;
double pA, pB;
res = sum = 0;
for (w = S.begin (); w != S.end (); w++)
{
if (w->second >= S.seuil)
{
pA = p_directe (w->first);
pB = B.p_directe (w->first);
res += (pA - pB) * (pA - pB) * pA;
sum += pA;
}
}
return sqrt (res / sum);
}
// renvoie la norme L1 entre ce PFA et un autre
double
PFA::L_1nlog (const PFA & B, const Sample & S) const
{
Sample::const_iterator w;
double res, sum;
double pA, pB;
res = sum = 0;
for (w = S.begin (); w != S.end (); w++)
{
if (w->second >= S.seuil)
{
pA = p_directe (w->first);
pB = B.p_directe (w->first);
res += (pA - pB >= 0) ? (pA - pB) * pA : (pB - pA) * pA; // |pA(w) - pB(w)|*pA(w)
sum += pA;
}
}
return sqrt (res / sum);
}
// renvoie la moyenne des Ècarts de log en fonction d'un Èchantillon S
double
PFA::dlog (const PFA & B, const Sample & S) const
{
Sample::const_iterator w;
double res, sum, pa;
res = sum = 0;
for (w = S.begin (); w != S.end (); w++)
{
sum += w->second;
pa = plog (w->first) - B.plog (w->first);
if (pa < 0)
pa = -pa;
res += pa * w->second;
}
return res / sum;
}
// renvoie la moyenne des Ècarts de log en fonction d'un Èchantillon S
double
PFA::dlog_safe (const PFA & B, const Sample & S) const
{
Sample::const_iterator w;
double res, pa;
int sum, nb_err, nb_mots;
res = sum = nb_err = nb_mots = 0;
for (w = S.begin (); w != S.end (); w++)
{
if (w->second >= S.seuil)
{
nb_mots++;
pa = plog (w->first) - B.plog (w->first);
if (!isnan (pa))
{
sum += w->second;
if (pa < 0)
pa = -pa;
res += pa * w->second;
}
else
{
if (PFA_VERBOSE)
{
cerr << "\tErreur : ";
Word::const_iterator va;
for (va = w->first.begin ();
va != w->first.end (); va++)
cerr << alph.find (*va)->
second << " ";
cerr << endl;
cerr << "\tpA = " << plog (w->
first) <<
" et pB = " << B.plog (w->
first)
<< endl;
}
nb_err++;
}
}
}
if (PFA_VERBOSE)
{
if (nb_err > 0)
cerr << nb_err * 100 /
nb_mots << "% d'erreur de structure." << endl;
}
return res / sum;
}
// renvoie la divergence de Kullback Leibler relativemant ‡ l'Èchantillon S
double
PFA::divKL (const PFA & B, const Sample & S)
{
return B.perplexite (S) - perplexite (S);
}
// return true if it is a PRFA
bool
PFA::isPRFA (bool verbose)
{
if (!isPFA())
{
return false;
}
// !!!!!!!!!!! VERSION NON DEFINITIVE !!!!!!!!!!!!!!
// !!!!! reduce is not yet implemented !!!!!!!!!
//reduce();
map < State, bool > resStates; // resStates[q]=true iff q is reach uniquely by some word.
set
< StateSet> Memory; // the set of states even founded.
StateSet QI; // l'ensemble des Ètats initiaux
StateSet R; // Un ensemble d'Ètats
StateSet Qm; // l'ensemble des Ètats dont on a dÈja trouvÈ un mot caractÈristique.
set
< StateSet >::const_iterator Ri;
set
< Word > W; // un ensemble de mots
Word w;
Word::iterator b;
StateSet::const_iterator q;
SFunc::iterator r;
Alphabet::const_iterator a;
//~ // -- //
//~ cout << "L'ensemble d'Ètats est : {";
//~ StateSet::const_iterator s;
//~ for (s=Q.begin() ; s != Q.end() ; ++s)
//~ cout << *s << ",";
//~ cout << "}" << endl;
//~ // -- //
// QI devient l'ensemble des Ètats initiaux
for (r=iota.begin();r != iota.end() ; ++r)
{
QI.insert(r->first);
}
//~ // -- //
//~ cout << "QI={";
//~ for (s=Q.begin() ; s != Q.end() ; ++s)
//~ cout << *s << ",";
//~ cout << "}" << endl;
//~ // -- //
// W = Sigma
for (a=Sigma.begin() ; a != Sigma.end() ; ++a)
{
w.clear();
w += *a;
//w.push_back(*a);
W.insert(w);
}
// Si il n'y a qu'un seul Ètat initial, alors
// son mot caractÈristique est epsilon
if (QI.size()==1)
{
Qm=QI;
if (verbose)
{
cout << "mot caractÈristique de " << *(Qm.begin()) << "\n";
cout << "epsilon\n";
cout << "longueur 0" << endl;
}
}
// QI est la premiËre partie possible
Memory.insert(QI);
while(!W.empty() && !(Qm==Q))
{
w=*(W.begin()); // w=min W
W.erase(W.begin()); // W <- W \ {w}
R=delta(QI,w);
//~ // -- Affichage de delta (QI, w) -- //
//~ cout << "delta(QI," << affiche(w) << ")={";
//~ for (s = R.begin() ; s != R.end() ; ++s)
//~ cout << *s << ",";
//~ cout << "}" << endl;
//~ // -- //
// si R \notin Memory
if (Memory.find(R) == Memory.end())
{
// on rajoute R ‡ Memory
Memory.insert(R);
// W = W \cup w\Sigma
for (a=Sigma.begin() ; a != Sigma.end() ; ++a)
{
w += *a;
//w.push_back(*a);
W.insert(w);
b=w.end();
w.erase(--b);
}
// Si R contient un seul Ètat
if (R.size() == 1)
{
// Si cet Ètat n'a pas dÈja eu un mot caractÈristique
if (Qm.find(*(R.begin())) == Qm.end())
{
// Alors on a trouver un nouvel Ètat dont on connait le
// mot caractÈristique (w)
Qm.insert(*(R.begin()));
if (verbose)
{
cout << "le mot caracteristique de l'Ètat " << *(R.begin()) << " est :\n";
cout << affiche(w) << "\n";
cout << "longueur " << w.size() << endl;
}
}
}
}
}
return Qm==Q;
}
bool PFA::isPDFA()
{
if (!isPFA())
{
return false;
}
erase_transitions(0,0); // erasing null transition
if (iota.size() != 1)
return false;
Graph::const_iterator q;
LettreSFunc::const_iterator a;
SFunc::const_iterator r;
for (q=phi.begin() ; q != phi.end() ; ++q)
{
for (a=q->second.begin() ; a!=q->second.end() ; ++a)
{
if (a->second.size() > 1)
return false;
}
}
return true;
}