Source code for annotations.Align.models.acm.tiedlist
"""
:filename: sppas.src.annotations.Align.models.acm.tiedlist.py
:author: Brigitte Bigi
:contact: develop@sppas.org
:summary: Tiedlist of an HTK acoustic models.
.. _This file is part of SPPAS: http://www.sppas.org/
..
-------------------------------------------------------------------------
___ __ __ __ ___
/ | \ | \ | \ / the automatic
\__ |__/ |__/ |___| \__ annotation and
\ | | | | \ analysis
___/ | | | | ___/ of speech
Copyright (C) 2011-2021 Brigitte Bigi
Laboratoire Parole et Langage, Aix-en-Provence, France
Use of this software is governed by the GNU Public License, version 3.
SPPAS 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 3 of the License, or
(at your option) any later version.
SPPAS 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 SPPAS. If not, see <http://www.gnu.org/licenses/>.
This banner notice must not be removed.
-------------------------------------------------------------------------
"""
import codecs
from sppas.src.config import sg
# ---------------------------------------------------------------------------
[docs]class sppasTiedList(object):
"""Tiedlist of an acoustic model.
This class is used to manage the tiedlist of a triphone acoustic model,
i.e:
- the list of observed phones, biphones, triphones,
- a list of biphones or triphones to tie.
"""
[docs] def __init__(self):
"""Create a sppasTiedList instance."""
self.observed = list()
self.tied = dict()
# -----------------------------------------------------------------------
[docs] def read(self, filename):
"""Read a tiedlist from a file and set it.
:param filename: (str)
"""
with codecs.open(filename, 'r', sg.__encoding__) as fd:
for nbl, line in enumerate(fd, 1):
line = line.strip()
try:
tab = line.split(' ')
if len(tab) == 1:
self.add_observed(line)
elif len(tab) == 2:
self.add_tied(tab[0].strip(), tab[1].strip())
else:
raise ValueError('Unexpected entry at line %d: %r'
'' % (nbl, tab))
except Exception as e:
raise IOError("Read file failed due to the following "
"error at line %d: %s" % (nbl, str(e)))
# -----------------------------------------------------------------------
[docs] def save(self, filename):
"""Save the tiedlist into a file.
:param filename: Name of the file of the tiedlist
"""
with codecs.open(filename, 'w', sg.__encoding__) as fp:
for triphone in self.observed:
fp.write(triphone + "\n")
for k, v in sorted(self.tied.items()):
fp.write(k + " " + v + "\n")
# -----------------------------------------------------------------------
[docs] def is_empty(self):
"""Return True if the tiedlist is empty."""
return len(self.observed) == 0 and len(self.tied) == 0
# -----------------------------------------------------------------------
[docs] def is_observed(self, entry):
"""Return True if entry is really observed (not tied!).
:param entry: (str) triphone/biphone/monophone
"""
return entry in self.observed
# -----------------------------------------------------------------------
[docs] def is_tied(self, entry):
"""Return True if entry is tied.
:param entry: (str) a triphone/biphone/monophone
"""
return entry in self.tied
# -----------------------------------------------------------------------
[docs] def add_tied(self, tied, observed=None):
"""Add an entry into the tiedlist.
If observed is None, an heuristic will assign one.
:param tied: (str) the biphone/triphone to add,
:param observed: (str) the biphone/triphone to tie with.
:returns: bool
"""
if tied in self.tied or tied in self.observed:
return False
if observed is None:
# Which type of entry?
if tied.find("+") == -1:
# NOT either a biphone or triphone
return False
if tied.find("-") == -1:
return self.__add_biphone(tied)
return self.__add_triphone(tied)
self.tied[tied] = observed
return True
# -----------------------------------------------------------------------
[docs] def add_to_tie(self, entries):
"""Add several un-observed entries in the tiedlist.
:param entries: (list)
:returns: list of entries really added into the tiedlist
"""
add_entries = list()
for entry in entries:
if self.is_observed(entry) is False and \
self.is_tied(entry) is False:
ret = self.add_tied(entry)
if ret is True:
add_entries.append(entry)
return add_entries
# -----------------------------------------------------------------------
[docs] def add_observed(self, entry):
"""Add an observed entry.
:param entry: (str)
:returns: bool
"""
if entry not in self.observed:
self.observed.append(entry)
return True
return False
# -----------------------------------------------------------------------
[docs] def merge(self, other):
"""Merge self with another tiedlist.
:param other: (sppasTiedList)
"""
if isinstance(other, sppasTiedList) is False:
raise TypeError('A sppasTiedList can only be merged with '
'another sppasTiedList. '
'Got {:s}.'.format(type(other)))
for obs in other.observed:
self.add_observed(obs)
for tie, obs in other.tied.items():
self.add_tied(tie, obs)
# -----------------------------------------------------------------------
[docs] def remove(self, entry, propagate=False):
"""Remove an entry of the list of observed or tied entries.
:param entry: (str) the entry to be removed
:param propagate: (bool) if entry is an observed item, remove all tied
that are using this observed item.
"""
if entry in self.observed:
self.observed.remove(entry)
if propagate is True:
for k, v in self.tied.items():
if v == entry:
self.tied.pop(k)
if entry in self.tied.keys():
self.tied.pop(entry)
# -----------------------------------------------------------------------
# Private
# -----------------------------------------------------------------------
def __find(self, tied):
"""Find which observed model will match to tie the given entry.
:param tied: (str) the model to be tied
:returns: the observed model to tie with.
"""
observed = ""
frqtied = {}
for v in self.tied.values():
if v.find(tied) > -1:
# Caution: a biphone can only be tied with a biphone
if tied.find("-") == -1 and v.find("-") > -1:
pass
else:
if v in frqtied:
frqtied[v] = frqtied[v] + 1
else:
frqtied[v] = 1
frqmax = 0
for p, f in frqtied.items():
if f > frqmax:
frqmax = f
observed = p
return observed
# -----------------------------------------------------------------------
def __add_triphone(self, entry):
"""Add an observed model to tie with the given entry.
:param entry: (str) the model to be tied
:returns: (bool)
"""
# Get the biphone to tie
biphone = entry[entry.find('-')+1:]
observed = self.__find(biphone)
if len(observed) == 0:
# Get the monophone to tie
monophone = entry[entry.find('-'):entry.find('+')+1]
observed = self.__find(monophone)
if len(observed) == 0:
return False
self.tied[entry] = observed
return True
# -----------------------------------------------------------------------
def __add_biphone(self, entry):
"""Add an observed model to tie with the given entry.
:param entry: (str) the model to be tied
:returns: (bool)
"""
# Get the monophone to tie
monophone = entry[:entry.find('+')+1]
observed = self.__find(monophone)
if len(observed) == 0:
return False
self.tied[entry] = observed
return True