diff options
Diffstat (limited to 'src/test')
-rw-r--r-- | src/test/Makefile.am | 12 | ||||
-rw-r--r-- | src/test/Makefile.in | 608 | ||||
-rw-r--r-- | src/test/algo_test.cc | 155 | ||||
-rw-r--r-- | src/test/algo_test.h | 1315 | ||||
-rw-r--r-- | src/test/fst_test.cc | 228 | ||||
-rw-r--r-- | src/test/fst_test.h | 299 | ||||
-rw-r--r-- | src/test/weight-tester.h | 225 | ||||
-rw-r--r-- | src/test/weight_test.cc | 258 |
8 files changed, 3100 insertions, 0 deletions
diff --git a/src/test/Makefile.am b/src/test/Makefile.am new file mode 100644 index 0000000..1456436 --- /dev/null +++ b/src/test/Makefile.am @@ -0,0 +1,12 @@ +AM_CPPFLAGS = -I$(srcdir)/../include $(ICU_CPPFLAGS) +LDADD = ../lib/libfst.la -lm -ldl + +check_PROGRAMS = fst_test weight_test algo_test + +fst_test_SOURCES = fst_test.cc fst_test.h + +weight_test_SOURCES = weight_test.cc weight-tester.h + +algo_test_SOURCES = algo_test.cc algo_test.h + +TESTS = fst_test weight_test algo_test diff --git a/src/test/Makefile.in b/src/test/Makefile.in new file mode 100644 index 0000000..00fe859 --- /dev/null +++ b/src/test/Makefile.in @@ -0,0 +1,608 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +check_PROGRAMS = fst_test$(EXEEXT) weight_test$(EXEEXT) \ + algo_test$(EXEEXT) +TESTS = fst_test$(EXEEXT) weight_test$(EXEEXT) algo_test$(EXEEXT) +subdir = src/test +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_icu.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h \ + $(top_builddir)/src/include/fst/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am_algo_test_OBJECTS = algo_test.$(OBJEXT) +algo_test_OBJECTS = $(am_algo_test_OBJECTS) +algo_test_LDADD = $(LDADD) +algo_test_DEPENDENCIES = ../lib/libfst.la +am_fst_test_OBJECTS = fst_test.$(OBJEXT) +fst_test_OBJECTS = $(am_fst_test_OBJECTS) +fst_test_LDADD = $(LDADD) +fst_test_DEPENDENCIES = ../lib/libfst.la +am_weight_test_OBJECTS = weight_test.$(OBJEXT) +weight_test_OBJECTS = $(am_weight_test_OBJECTS) +weight_test_LDADD = $(LDADD) +weight_test_DEPENDENCIES = ../lib/libfst.la +DEFAULT_INCLUDES = +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(algo_test_SOURCES) $(fst_test_SOURCES) \ + $(weight_test_SOURCES) +DIST_SOURCES = $(algo_test_SOURCES) $(fst_test_SOURCES) \ + $(weight_test_SOURCES) +ETAGS = etags +CTAGS = ctags +am__tty_colors = \ +red=; grn=; lgn=; blu=; std= +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +ICU_CFLAGS = @ICU_CFLAGS@ +ICU_CONFIG = @ICU_CONFIG@ +ICU_CPPFLAGS = @ICU_CPPFLAGS@ +ICU_CXXFLAGS = @ICU_CXXFLAGS@ +ICU_LIBS = @ICU_LIBS@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +libfstdir = @libfstdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +lt_ECHO = @lt_ECHO@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +AM_CPPFLAGS = -I$(srcdir)/../include $(ICU_CPPFLAGS) +LDADD = ../lib/libfst.la -lm -ldl +fst_test_SOURCES = fst_test.cc fst_test.h +weight_test_SOURCES = weight_test.cc weight-tester.h +algo_test_SOURCES = algo_test.cc algo_test.h +all: all-am + +.SUFFIXES: +.SUFFIXES: .cc .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/test/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/test/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-checkPROGRAMS: + @list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list +algo_test$(EXEEXT): $(algo_test_OBJECTS) $(algo_test_DEPENDENCIES) + @rm -f algo_test$(EXEEXT) + $(CXXLINK) $(algo_test_OBJECTS) $(algo_test_LDADD) $(LIBS) +fst_test$(EXEEXT): $(fst_test_OBJECTS) $(fst_test_DEPENDENCIES) + @rm -f fst_test$(EXEEXT) + $(CXXLINK) $(fst_test_OBJECTS) $(fst_test_LDADD) $(LIBS) +weight_test$(EXEEXT): $(weight_test_OBJECTS) $(weight_test_DEPENDENCIES) + @rm -f weight_test$(EXEEXT) + $(CXXLINK) $(weight_test_OBJECTS) $(weight_test_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/algo_test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fst_test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/weight_test.Po@am__quote@ + +.cc.o: +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $< + +.cc.obj: +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cc.lo: +@am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +check-TESTS: $(TESTS) + @failed=0; all=0; xfail=0; xpass=0; skip=0; \ + srcdir=$(srcdir); export srcdir; \ + list=' $(TESTS) '; \ + $(am__tty_colors); \ + if test -n "$$list"; then \ + for tst in $$list; do \ + if test -f ./$$tst; then dir=./; \ + elif test -f $$tst; then dir=; \ + else dir="$(srcdir)/"; fi; \ + if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xpass=`expr $$xpass + 1`; \ + failed=`expr $$failed + 1`; \ + col=$$red; res=XPASS; \ + ;; \ + *) \ + col=$$grn; res=PASS; \ + ;; \ + esac; \ + elif test $$? -ne 77; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xfail=`expr $$xfail + 1`; \ + col=$$lgn; res=XFAIL; \ + ;; \ + *) \ + failed=`expr $$failed + 1`; \ + col=$$red; res=FAIL; \ + ;; \ + esac; \ + else \ + skip=`expr $$skip + 1`; \ + col=$$blu; res=SKIP; \ + fi; \ + echo "$${col}$$res$${std}: $$tst"; \ + done; \ + if test "$$all" -eq 1; then \ + tests="test"; \ + All=""; \ + else \ + tests="tests"; \ + All="All "; \ + fi; \ + if test "$$failed" -eq 0; then \ + if test "$$xfail" -eq 0; then \ + banner="$$All$$all $$tests passed"; \ + else \ + if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \ + banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \ + fi; \ + else \ + if test "$$xpass" -eq 0; then \ + banner="$$failed of $$all $$tests failed"; \ + else \ + if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \ + banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \ + fi; \ + fi; \ + dashes="$$banner"; \ + skipped=""; \ + if test "$$skip" -ne 0; then \ + if test "$$skip" -eq 1; then \ + skipped="($$skip test was not run)"; \ + else \ + skipped="($$skip tests were not run)"; \ + fi; \ + test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$skipped"; \ + fi; \ + report=""; \ + if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ + report="Please report to $(PACKAGE_BUGREPORT)"; \ + test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$report"; \ + fi; \ + dashes=`echo "$$dashes" | sed s/./=/g`; \ + if test "$$failed" -eq 0; then \ + echo "$$grn$$dashes"; \ + else \ + echo "$$red$$dashes"; \ + fi; \ + echo "$$banner"; \ + test -z "$$skipped" || echo "$$skipped"; \ + test -z "$$report" || echo "$$report"; \ + echo "$$dashes$$std"; \ + test "$$failed" -eq 0; \ + else :; fi + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +check: check-am +all-am: Makefile +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-checkPROGRAMS clean-generic clean-libtool \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: check-am install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-TESTS check-am clean \ + clean-checkPROGRAMS clean-generic clean-libtool ctags \ + distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags uninstall uninstall-am + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/test/algo_test.cc b/src/test/algo_test.cc new file mode 100644 index 0000000..9538d54 --- /dev/null +++ b/src/test/algo_test.cc @@ -0,0 +1,155 @@ +// algo_test.h + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Copyright 2005-2010 Google, Inc. +// Author: riley@google.com (Michael Riley) +// +// \file +// Regression test for various FST algorithms. + +#include "./algo_test.h" + +// These determine which semirings are tested. Defining at least +// TEST_TROPICAL and TEST_LOG is recommended. More increase the +// comprehensiveness, but also increase the compilation time. + +#define TEST_TROPICAL +#define TEST_LOG +// #define TEST_MINMAX +// #define TEST_LEFT_STRING +// #define TEST_RIGHT_STRING +// #define TEST_GALLIC +// #define TEST_LEXICOGRAPHIC +// #define TEST_POWER + +DEFINE_int32(seed, -1, "random seed"); +DEFINE_int32(repeat, 25, "number of test repetitions"); + +using fst::StdArc; +using fst::TropicalWeightGenerator; + +using fst::LogArc; +using fst::LogWeightGenerator; + +using fst::MinMaxArc; +using fst::MinMaxWeightGenerator; + +using fst::StringArc; +using fst::StringWeightGenerator; +using fst::STRING_LEFT; +using fst::STRING_RIGHT; + +using fst::GallicArc; +using fst::GallicWeightGenerator; + +using fst::LexicographicArc; +using fst::TropicalWeight; +using fst::LexicographicWeightGenerator; + +using fst::ArcTpl; +using fst::PowerWeight; +using fst::PowerWeightGenerator; + +using fst::AlgoTester; + +int main(int argc, char **argv) { + FLAGS_fst_verify_properties = true; + std::set_new_handler(FailedNewHandler); + SetFlags(argv[0], &argc, &argv, true); + + static const int kCacheGcLimit = 20; + + int seed = FLAGS_seed >= 0 ? FLAGS_seed : time(0); + srand(seed); + LOG(INFO) << "Seed = " << seed; + + FLAGS_fst_default_cache_gc = rand() % 2; + FLAGS_fst_default_cache_gc_limit = rand() % kCacheGcLimit; + VLOG(1) << "default_cache_gc:" << FLAGS_fst_default_cache_gc; + VLOG(1) << "default_cache_gc_limit:" << FLAGS_fst_default_cache_gc_limit; + +#ifdef TEST_TROPICAL + TropicalWeightGenerator tropical_generator(seed, false); + AlgoTester<StdArc, TropicalWeightGenerator> + tropical_tester(tropical_generator, seed); + tropical_tester.Test(); +#endif // TEST_TROPICAL + +#ifdef TEST_LOG + LogWeightGenerator log_generator(seed, false); + AlgoTester<LogArc, LogWeightGenerator> + log_tester(log_generator, seed); + log_tester.Test(); +#endif // TEST_LOG + +#ifdef TEST_MINMAX + MinMaxWeightGenerator minmax_generator(seed, false); + AlgoTester<MinMaxArc, MinMaxWeightGenerator> + minmax_tester(minmax_generator, seed); + minmax_tester.Test(); +#endif + +#ifdef TEST_LEFT_STRING + StringWeightGenerator<int> left_string_generator(seed, false); + AlgoTester<StringArc<>, StringWeightGenerator<int> > + left_string_tester(left_string_generator, seed); + left_string_tester.Test(); +#endif // TEST_LEFT_STRING + +#ifdef TEST_RIGHT_STRING + StringWeightGenerator<int, STRING_RIGHT> right_string_generator(seed, false); + AlgoTester<StringArc<STRING_RIGHT>, + StringWeightGenerator<int, STRING_RIGHT> > + right_string_tester(right_string_generator, seed); + right_string_tester.Test(); +#endif // TEST_RIGHT_STRING + +#ifdef TEST_GALLIC + typedef GallicArc<StdArc> StdGallicArc; + typedef GallicWeightGenerator<int, TropicalWeightGenerator> + TropicalGallicWeightGenerator; + + TropicalGallicWeightGenerator tropical_gallic_generator(seed, false); + AlgoTester<StdGallicArc, TropicalGallicWeightGenerator> + gallic_tester(tropical_gallic_generator, seed); + gallic_tester.Test(); +#endif // TEST_GALLIC + +#ifdef TEST_LEXICOGRAPHIC + typedef LexicographicArc<TropicalWeight, TropicalWeight> + TropicalLexicographicArc; + typedef LexicographicWeightGenerator<TropicalWeightGenerator, + TropicalWeightGenerator> TropicalLexicographicWeightGenerator; + TropicalLexicographicWeightGenerator lexicographic_generator(seed, false); + AlgoTester<TropicalLexicographicArc, TropicalLexicographicWeightGenerator> + lexicographic_tester(lexicographic_generator, seed); + lexicographic_tester.Test(); +#endif // TEST_LEXICOGRAPHIC + +#ifdef TEST_POWER + typedef PowerWeight<TropicalWeight, 3> TropicalCubeWeight; + typedef ArcTpl<TropicalCubeWeight> TropicalCubeArc; + typedef PowerWeightGenerator<TropicalWeightGenerator, 3> + TropicalCubeWeightGenerator; + + TropicalCubeWeightGenerator tropical_cube_generator(seed, false); + AlgoTester<TropicalCubeArc, TropicalCubeWeightGenerator> + tropical_cube_tester(tropical_cube_generator, seed); + tropical_cube_tester.Test(); +#endif // TEST_POWER + + cout << "PASS" << endl; + + return 0; +} diff --git a/src/test/algo_test.h b/src/test/algo_test.h new file mode 100644 index 0000000..3aca3cc --- /dev/null +++ b/src/test/algo_test.h @@ -0,0 +1,1315 @@ +// algo_test.h + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Copyright 2005-2010 Google, Inc. +// Author: riley@google.com (Michael Riley) +// +// \file +// Regression test for various FST algorithms. + +#ifndef FST_TEST_ALGO_TEST_H__ +#define FST_TEST_ALGO_TEST_H__ + +#include <fst/fstlib.h> +#include <fst/random-weight.h> + +DECLARE_int32(repeat); // defined in ./algo_test.cc + +namespace fst { + +// Mapper to change input and output label of every transition into +// epsilons. +template <class A> +class EpsMapper { + public: + EpsMapper() {} + + A operator()(const A &arc) const { + return A(0, 0, arc.weight, arc.nextstate); + } + + uint64 Properties(uint64 props) const { + props &= ~kNotAcceptor; + props |= kAcceptor; + props &= ~kNoIEpsilons & ~kNoOEpsilons & ~kNoEpsilons; + props |= kIEpsilons | kOEpsilons | kEpsilons; + props &= ~kNotILabelSorted & ~kNotOLabelSorted; + props |= kILabelSorted | kOLabelSorted; + return props; + } + + MapFinalAction FinalAction() const { return MAP_NO_SUPERFINAL; } + + + MapSymbolsAction InputSymbolsAction() const { return MAP_COPY_SYMBOLS;} + + MapSymbolsAction OutputSymbolsAction() const { return MAP_COPY_SYMBOLS;} +}; + +// This class tests a variety of identities and properties that must +// hold for various algorithms on weighted FSTs. +template <class Arc, class WeightGenerator> +class WeightedTester { + public: + typedef typename Arc::Label Label; + typedef typename Arc::StateId StateId; + typedef typename Arc::Weight Weight; + + WeightedTester(int seed, const Fst<Arc> &zero_fst, const Fst<Arc> &one_fst, + const Fst<Arc> &univ_fst, WeightGenerator *weight_generator) + : seed_(seed), zero_fst_(zero_fst), one_fst_(one_fst), + univ_fst_(univ_fst), weight_generator_(weight_generator) {} + + void Test(const Fst<Arc> &T1, const Fst<Arc> &T2, const Fst<Arc> &T3) { + TestRational(T1, T2, T3); + TestMap(T1); + TestCompose(T1, T2, T3); + TestSort(T1); + TestOptimize(T1); + TestSearch(T1); + } + + private: + // Tests rational operations with identities + void TestRational(const Fst<Arc> &T1, const Fst<Arc> &T2, + const Fst<Arc> &T3) { + + { + VLOG(1) << "Check destructive and delayed union are equivalent."; + VectorFst<Arc> U1(T1); + Union(&U1, T2); + UnionFst<Arc> U2(T1, T2); + CHECK(Equiv(U1, U2)); + } + + + { + VLOG(1) << "Check destructive and delayed concatenation are equivalent."; + VectorFst<Arc> C1(T1); + Concat(&C1, T2); + ConcatFst<Arc> C2(T1, T2); + CHECK(Equiv(C1, C2)); + VectorFst<Arc> C3(T2); + Concat(T1, &C3); + CHECK(Equiv(C3, C2)); + } + + { + VLOG(1) << "Check destructive and delayed closure* are equivalent."; + VectorFst<Arc> C1(T1); + Closure(&C1, CLOSURE_STAR); + ClosureFst<Arc> C2(T1, CLOSURE_STAR); + CHECK(Equiv(C1, C2)); + } + + { + VLOG(1) << "Check destructive and delayed closure+ are equivalent."; + VectorFst<Arc> C1(T1); + Closure(&C1, CLOSURE_PLUS); + ClosureFst<Arc> C2(T1, CLOSURE_PLUS); + CHECK(Equiv(C1, C2)); + } + + { + VLOG(1) << "Check union is associative (destructive)."; + VectorFst<Arc> U1(T1); + Union(&U1, T2); + Union(&U1, T3); + + VectorFst<Arc> U3(T2); + Union(&U3, T3); + VectorFst<Arc> U4(T1); + Union(&U4, U3); + + CHECK(Equiv(U1, U4)); + } + + { + VLOG(1) << "Check union is associative (delayed)."; + UnionFst<Arc> U1(T1, T2); + UnionFst<Arc> U2(U1, T3); + + UnionFst<Arc> U3(T2, T3); + UnionFst<Arc> U4(T1, U3); + + CHECK(Equiv(U2, U4)); + } + + + { + VLOG(1) << "Check union is associative (destructive delayed)."; + UnionFst<Arc> U1(T1, T2); + Union(&U1, T3); + + UnionFst<Arc> U3(T2, T3); + UnionFst<Arc> U4(T1, U3); + + CHECK(Equiv(U1, U4)); + } + + { + VLOG(1) << "Check concatenation is associative (destructive)."; + VectorFst<Arc> C1(T1); + Concat(&C1, T2); + Concat(&C1, T3); + + VectorFst<Arc> C3(T2); + Concat(&C3, T3); + VectorFst<Arc> C4(T1); + Concat(&C4, C3); + + CHECK(Equiv(C1, C4)); + } + + { + VLOG(1) << "Check concatenation is associative (delayed)."; + ConcatFst<Arc> C1(T1, T2); + ConcatFst<Arc> C2(C1, T3); + + ConcatFst<Arc> C3(T2, T3); + ConcatFst<Arc> C4(T1, C3); + + CHECK(Equiv(C2, C4)); + } + + { + VLOG(1) << "Check concatenation is associative (destructive delayed)."; + ConcatFst<Arc> C1(T1, T2); + Concat(&C1, T3); + + ConcatFst<Arc> C3(T2, T3); + ConcatFst<Arc> C4(T1, C3); + + CHECK(Equiv(C1, C4)); + } + + if (Weight::Properties() & kLeftSemiring) { + VLOG(1) << "Check concatenation left distributes" + << " over union (destructive)."; + + VectorFst<Arc> U1(T1); + Union(&U1, T2); + VectorFst<Arc> C1(T3); + Concat(&C1, U1); + + VectorFst<Arc> C2(T3); + Concat(&C2, T1); + VectorFst<Arc> C3(T3); + Concat(&C3, T2); + VectorFst<Arc> U2(C2); + Union(&U2, C3); + + CHECK(Equiv(C1, U2)); + } + + if (Weight::Properties() & kRightSemiring) { + VLOG(1) << "Check concatenation right distributes" + << " over union (destructive)."; + VectorFst<Arc> U1(T1); + Union(&U1, T2); + VectorFst<Arc> C1(U1); + Concat(&C1, T3); + + VectorFst<Arc> C2(T1); + Concat(&C2, T3); + VectorFst<Arc> C3(T2); + Concat(&C3, T3); + VectorFst<Arc> U2(C2); + Union(&U2, C3); + + CHECK(Equiv(C1, U2)); + } + + if (Weight::Properties() & kLeftSemiring) { + VLOG(1) << "Check concatenation left distributes over union (delayed)."; + UnionFst<Arc> U1(T1, T2); + ConcatFst<Arc> C1(T3, U1); + + ConcatFst<Arc> C2(T3, T1); + ConcatFst<Arc> C3(T3, T2); + UnionFst<Arc> U2(C2, C3); + + CHECK(Equiv(C1, U2)); + } + + if (Weight::Properties() & kRightSemiring) { + VLOG(1) << "Check concatenation right distributes over union (delayed)."; + UnionFst<Arc> U1(T1, T2); + ConcatFst<Arc> C1(U1, T3); + + ConcatFst<Arc> C2(T1, T3); + ConcatFst<Arc> C3(T2, T3); + UnionFst<Arc> U2(C2, C3); + + CHECK(Equiv(C1, U2)); + } + + + if (Weight::Properties() & kLeftSemiring) { + VLOG(1) << "Check T T* == T+ (destructive)."; + VectorFst<Arc> S(T1); + Closure(&S, CLOSURE_STAR); + VectorFst<Arc> C(T1); + Concat(&C, S); + + VectorFst<Arc> P(T1); + Closure(&P, CLOSURE_PLUS); + + CHECK(Equiv(C, P)); + } + + + if (Weight::Properties() & kRightSemiring) { + VLOG(1) << "Check T* T == T+ (destructive)."; + VectorFst<Arc> S(T1); + Closure(&S, CLOSURE_STAR); + VectorFst<Arc> C(S); + Concat(&C, T1); + + VectorFst<Arc> P(T1); + Closure(&P, CLOSURE_PLUS); + + CHECK(Equiv(C, P)); + } + + if (Weight::Properties() & kLeftSemiring) { + VLOG(1) << "Check T T* == T+ (delayed)."; + ClosureFst<Arc> S(T1, CLOSURE_STAR); + ConcatFst<Arc> C(T1, S); + + ClosureFst<Arc> P(T1, CLOSURE_PLUS); + + CHECK(Equiv(C, P)); + } + + if (Weight::Properties() & kRightSemiring) { + VLOG(1) << "Check T* T == T+ (delayed)."; + ClosureFst<Arc> S(T1, CLOSURE_STAR); + ConcatFst<Arc> C(S, T1); + + ClosureFst<Arc> P(T1, CLOSURE_PLUS); + + CHECK(Equiv(C, P)); + } + } + + // Tests map-based operations. + void TestMap(const Fst<Arc> &T) { + + { + VLOG(1) << "Check destructive and delayed projection are equivalent."; + VectorFst<Arc> P1(T); + Project(&P1, PROJECT_INPUT); + ProjectFst<Arc> P2(T, PROJECT_INPUT); + CHECK(Equiv(P1, P2)); + } + + + { + VLOG(1) << "Check destructive and delayed inversion are equivalent."; + VectorFst<Arc> I1(T); + Invert(&I1); + InvertFst<Arc> I2(T); + CHECK(Equiv(I1, I2)); + } + + { + VLOG(1) << "Check Pi_1(T) = Pi_2(T^-1) (destructive)."; + VectorFst<Arc> P1(T); + VectorFst<Arc> I1(T); + Project(&P1, PROJECT_INPUT); + Invert(&I1); + Project(&I1, PROJECT_OUTPUT); + CHECK(Equiv(P1, I1)); + } + + { + VLOG(1) << "Check Pi_2(T) = Pi_1(T^-1) (destructive)."; + VectorFst<Arc> P1(T); + VectorFst<Arc> I1(T); + Project(&P1, PROJECT_OUTPUT); + Invert(&I1); + Project(&I1, PROJECT_INPUT); + CHECK(Equiv(P1, I1)); + } + + { + VLOG(1) << "Check Pi_1(T) = Pi_2(T^-1) (delayed)."; + ProjectFst<Arc> P1(T, PROJECT_INPUT); + InvertFst<Arc> I1(T); + ProjectFst<Arc> P2(I1, PROJECT_OUTPUT); + CHECK(Equiv(P1, P2)); + } + + + { + VLOG(1) << "Check Pi_2(T) = Pi_1(T^-1) (delayed)."; + ProjectFst<Arc> P1(T, PROJECT_OUTPUT); + InvertFst<Arc> I1(T); + ProjectFst<Arc> P2(I1, PROJECT_INPUT); + CHECK(Equiv(P1, P2)); + } + + + { + VLOG(1) << "Check destructive relabeling"; + static const int kNumLabels = 10; + // set up relabeling pairs + vector<Label> labelset(kNumLabels); + for (size_t i = 0; i < kNumLabels; ++i) labelset[i] = i; + for (size_t i = 0; i < kNumLabels; ++i) { + swap(labelset[i], labelset[rand() % kNumLabels]); + } + + vector<pair<Label, Label> > ipairs1(kNumLabels); + vector<pair<Label, Label> > opairs1(kNumLabels); + for (size_t i = 0; i < kNumLabels; ++i) { + ipairs1[i] = make_pair(i, labelset[i]); + opairs1[i] = make_pair(labelset[i], i); + } + VectorFst<Arc> R(T); + Relabel(&R, ipairs1, opairs1); + + vector<pair<Label, Label> > ipairs2(kNumLabels); + vector<pair<Label, Label> > opairs2(kNumLabels); + for (size_t i = 0; i < kNumLabels; ++i) { + ipairs2[i] = make_pair(labelset[i], i); + opairs2[i] = make_pair(i, labelset[i]); + } + Relabel(&R, ipairs2, opairs2); + CHECK(Equiv(R, T)); + + VLOG(1) << "Check on-the-fly relabeling"; + RelabelFst<Arc> Rdelay(T, ipairs1, opairs1); + + RelabelFst<Arc> RRdelay(Rdelay, ipairs2, opairs2); + CHECK(Equiv(RRdelay, T)); + } + + { + VLOG(1) << "Check encoding/decoding (destructive)."; + VectorFst<Arc> D(T); + uint32 encode_props = 0; + if (rand() % 2) + encode_props |= kEncodeLabels; + if (rand() % 2) + encode_props |= kEncodeWeights; + EncodeMapper<Arc> encoder(encode_props, ENCODE); + Encode(&D, &encoder); + Decode(&D, encoder); + CHECK(Equiv(D, T)); + } + + { + VLOG(1) << "Check encoding/decoding (delayed)."; + uint32 encode_props = 0; + if (rand() % 2) + encode_props |= kEncodeLabels; + if (rand() % 2) + encode_props |= kEncodeWeights; + EncodeMapper<Arc> encoder(encode_props, ENCODE); + EncodeFst<Arc> E(T, &encoder); + VectorFst<Arc> Encoded(E); + DecodeFst<Arc> D(Encoded, encoder); + CHECK(Equiv(D, T)); + } + + { + VLOG(1) << "Check gallic mappers (constructive)."; + ToGallicMapper<Arc> to_mapper; + FromGallicMapper<Arc> from_mapper; + VectorFst< GallicArc<Arc> > G; + VectorFst<Arc> F; + ArcMap(T, &G, to_mapper); + ArcMap(G, &F, from_mapper); + CHECK(Equiv(T, F)); + } + + { + VLOG(1) << "Check gallic mappers (delayed)."; + ToGallicMapper<Arc> to_mapper; + FromGallicMapper<Arc> from_mapper; + ArcMapFst<Arc, GallicArc<Arc>, ToGallicMapper<Arc> > + G(T, to_mapper); + ArcMapFst<GallicArc<Arc>, Arc, FromGallicMapper<Arc> > + F(G, from_mapper); + CHECK(Equiv(T, F)); + } + } + + // Tests compose-based operations. + void TestCompose(const Fst<Arc> &T1, const Fst<Arc> &T2, + const Fst<Arc> &T3) { + if (!(Weight::Properties() & kCommutative)) + return; + + VectorFst<Arc> S1(T1); + VectorFst<Arc> S2(T2); + VectorFst<Arc> S3(T3); + + ILabelCompare<Arc> icomp; + OLabelCompare<Arc> ocomp; + + ArcSort(&S1, ocomp); + ArcSort(&S2, ocomp); + ArcSort(&S3, icomp); + + { + VLOG(1) << "Check composition is associative."; + ComposeFst<Arc> C1(S1, S2); + + ComposeFst<Arc> C2(C1, S3); + ComposeFst<Arc> C3(S2, S3); + ComposeFst<Arc> C4(S1, C3); + + CHECK(Equiv(C2, C4)); + } + + { + VLOG(1) << "Check composition left distributes over union."; + UnionFst<Arc> U1(S2, S3); + ComposeFst<Arc> C1(S1, U1); + + ComposeFst<Arc> C2(S1, S2); + ComposeFst<Arc> C3(S1, S3); + UnionFst<Arc> U2(C2, C3); + + CHECK(Equiv(C1, U2)); + } + + { + VLOG(1) << "Check composition right distributes over union."; + UnionFst<Arc> U1(S1, S2); + ComposeFst<Arc> C1(U1, S3); + + ComposeFst<Arc> C2(S1, S3); + ComposeFst<Arc> C3(S2, S3); + UnionFst<Arc> U2(C2, C3); + + CHECK(Equiv(C1, U2)); + } + + VectorFst<Arc> A1(S1); + VectorFst<Arc> A2(S2); + VectorFst<Arc> A3(S3); + Project(&A1, PROJECT_OUTPUT); + Project(&A2, PROJECT_INPUT); + Project(&A3, PROJECT_INPUT); + + { + VLOG(1) << "Check intersection is commutative."; + IntersectFst<Arc> I1(A1, A2); + IntersectFst<Arc> I2(A2, A1); + CHECK(Equiv(I1, I2)); + } + + { + VLOG(1) << "Check all epsilon filters leads to equivalent results."; + typedef Matcher< Fst<Arc> > M; + ComposeFst<Arc> C1(S1, S2); + ComposeFst<Arc> C2( + S1, S2, + ComposeFstOptions<Arc, M, AltSequenceComposeFilter<M> >()); + ComposeFst<Arc> C3( + S1, S2, + ComposeFstOptions<Arc, M, MatchComposeFilter<M> >()); + + CHECK(Equiv(C1, C2)); + CHECK(Equiv(C1, C3)); + } + } + + // Tests sorting operations + void TestSort(const Fst<Arc> &T) { + ILabelCompare<Arc> icomp; + OLabelCompare<Arc> ocomp; + + { + VLOG(1) << "Check arc sorted Fst is equivalent to its input."; + VectorFst<Arc> S1(T); + ArcSort(&S1, icomp); + CHECK(Equiv(T, S1)); + } + + { + VLOG(1) << "Check destructive and delayed arcsort are equivalent."; + VectorFst<Arc> S1(T); + ArcSort(&S1, icomp); + ArcSortFst< Arc, ILabelCompare<Arc> > S2(T, icomp); + CHECK(Equiv(S1, S2)); + } + + { + VLOG(1) << "Check ilabel sorting vs. olabel sorting with inversions."; + VectorFst<Arc> S1(T); + VectorFst<Arc> S2(T); + ArcSort(&S1, icomp); + Invert(&S2); + ArcSort(&S2, ocomp); + Invert(&S2); + CHECK(Equiv(S1, S2)); + } + + { + VLOG(1) << "Check topologically sorted Fst is equivalent to its input."; + VectorFst<Arc> S1(T); + TopSort(&S1); + CHECK(Equiv(T, S1)); + } + + { + VLOG(1) << "Check reverse(reverse(T)) = T"; + VectorFst< ReverseArc<Arc> > R1; + VectorFst<Arc> R2; + Reverse(T, &R1); + Reverse(R1, &R2); + CHECK(Equiv(T, R2)); + } + } + + // Tests optimization operations + void TestOptimize(const Fst<Arc> &T) { + uint64 tprops = T.Properties(kFstProperties, true); + uint64 wprops = Weight::Properties(); + + VectorFst<Arc> A(T); + Project(&A, PROJECT_INPUT); + + { + VLOG(1) << "Check connected FST is equivalent to its input."; + VectorFst<Arc> C1(T); + Connect(&C1); + CHECK(Equiv(T, C1)); + } + + if ((wprops & kSemiring) == kSemiring && + (tprops & kAcyclic || wprops & kIdempotent)) { + VLOG(1) << "Check epsilon-removed FST is equivalent to its input."; + VectorFst<Arc> R1(T); + RmEpsilon(&R1); + CHECK(Equiv(T, R1)); + + VLOG(1) << "Check destructive and delayed epsilon removal" + << "are equivalent."; + RmEpsilonFst<Arc> R2(T); + CHECK(Equiv(R1, R2)); + + VLOG(1) << "Check an FST with a large proportion" + << " of epsilon transitions:"; + // Maps all transitions of T to epsilon-transitions and append + // a non-epsilon transition. + VectorFst<Arc> U; + ArcMap(T, &U, EpsMapper<Arc>()); + VectorFst<Arc> V; + V.SetStart(V.AddState()); + Arc arc(1, 1, Weight::One(), V.AddState()); + V.AddArc(V.Start(), arc); + V.SetFinal(arc.nextstate, Weight::One()); + Concat(&U, V); + // Check that epsilon-removal preserves the shortest-distance + // from the initial state to the final states. + vector<Weight> d; + ShortestDistance(U, &d, true); + Weight w = U.Start() < d.size() ? d[U.Start()] : Weight::Zero(); + VectorFst<Arc> U1(U); + RmEpsilon(&U1); + ShortestDistance(U1, &d, true); + Weight w1 = U1.Start() < d.size() ? d[U1.Start()] : Weight::Zero(); + CHECK(ApproxEqual(w, w1, kTestDelta)); + RmEpsilonFst<Arc> U2(U); + ShortestDistance(U2, &d, true); + Weight w2 = U2.Start() < d.size() ? d[U2.Start()] : Weight::Zero(); + CHECK(ApproxEqual(w, w2, kTestDelta)); + } + + if ((wprops & kSemiring) == kSemiring && tprops & kAcyclic) { + VLOG(1) << "Check determinized FSA is equivalent to its input."; + DeterminizeFst<Arc> D(A); + CHECK(Equiv(A, D)); + + + int n; + { + VLOG(1) << "Check size(min(det(A))) <= size(det(A))" + << " and min(det(A)) equiv det(A)"; + VectorFst<Arc> M(D); + n = M.NumStates(); + Minimize(&M); + CHECK(Equiv(D, M)); + CHECK(M.NumStates() <= n); + n = M.NumStates(); + } + + if (n && (wprops & kIdempotent) == kIdempotent && + A.Properties(kNoEpsilons, true)) { + VLOG(1) << "Check that Revuz's algorithm leads to the" + << " same number of states as Brozozowski's algorithm"; + + // Skip test if A is the empty machine or contains epsilons or + // if the semiring is not idempotent (to avoid floating point + // errors) + VectorFst<Arc> R; + Reverse(A, &R); + RmEpsilon(&R); + DeterminizeFst<Arc> DR(R); + VectorFst<Arc> RD; + Reverse(DR, &RD); + DeterminizeFst<Arc> DRD(RD); + VectorFst<Arc> M(DRD); + CHECK_EQ(n + 1, M.NumStates()); // Accounts for the epsilon transition + // to the initial state + } + } + + if (Arc::Type() == LogArc::Type() || Arc::Type() == StdArc::Type()) { + VLOG(1) << "Check reweight(T) equiv T"; + vector<Weight> potential; + VectorFst<Arc> RI(T); + VectorFst<Arc> RF(T); + while (potential.size() < RI.NumStates()) + potential.push_back((*weight_generator_)()); + + Reweight(&RI, potential, REWEIGHT_TO_INITIAL); + CHECK(Equiv(T, RI)); + + Reweight(&RF, potential, REWEIGHT_TO_FINAL); + CHECK(Equiv(T, RF)); + } + + if ((wprops & kIdempotent) || (tprops & kAcyclic)) { + VLOG(1) << "Check pushed FST is equivalent to input FST."; + // Pushing towards the final state. + if (wprops & kRightSemiring) { + VectorFst<Arc> P1; + Push<Arc, REWEIGHT_TO_FINAL>(T, &P1, kPushLabels); + CHECK(Equiv(T, P1)); + + VectorFst<Arc> P2; + Push<Arc, REWEIGHT_TO_FINAL>(T, &P2, kPushWeights); + CHECK(Equiv(T, P2)); + + VectorFst<Arc> P3; + Push<Arc, REWEIGHT_TO_FINAL>(T, &P3, kPushLabels | kPushWeights); + CHECK(Equiv(T, P3)); + } + + // Pushing towards the initial state. + if (wprops & kLeftSemiring) { + VectorFst<Arc> P1; + Push<Arc, REWEIGHT_TO_INITIAL>(T, &P1, kPushLabels); + CHECK(Equiv(T, P1)); + + VectorFst<Arc> P2; + Push<Arc, REWEIGHT_TO_INITIAL>(T, &P2, kPushWeights); + CHECK(Equiv(T, P2)); + VectorFst<Arc> P3; + Push<Arc, REWEIGHT_TO_INITIAL>(T, &P3, kPushLabels | kPushWeights); + CHECK(Equiv(T, P3)); + } + } + + if ((wprops & (kPath | kCommutative)) == (kPath | kCommutative)) { + VLOG(1) << "Check pruning algorithm"; + { + VLOG(1) << "Check equiv. of constructive and destructive algorithms"; + Weight thresold = (*weight_generator_)(); + VectorFst<Arc> P1(T); + Prune(&P1, thresold); + VectorFst<Arc> P2; + Prune(T, &P2, thresold); + CHECK(Equiv(P1,P2)); + } + + { + VLOG(1) << "Check prune(reverse) equiv reverse(prune)"; + Weight thresold = (*weight_generator_)(); + VectorFst< ReverseArc<Arc> > R; + VectorFst<Arc> P1(T); + VectorFst<Arc> P2; + Prune(&P1, thresold); + Reverse(T, &R); + Prune(&R, thresold.Reverse()); + Reverse(R, &P2); + CHECK(Equiv(P1, P2)); + } + { + VLOG(1) << "Check: ShortestDistance(T- prune(T))" + << " > ShortestDistance(T) times Thresold"; + Weight thresold = (*weight_generator_)(); + VectorFst<Arc> P; + Prune(A, &P, thresold); + DifferenceFst<Arc> C(A, DeterminizeFst<Arc> + (RmEpsilonFst<Arc> + (ArcMapFst<Arc, Arc, + RmWeightMapper<Arc> > + (P, RmWeightMapper<Arc>())))); + Weight sum1 = Times(ShortestDistance(A), thresold); + Weight sum2 = ShortestDistance(C); + CHECK(Plus(sum1, sum2) == sum1); + } + } + if (tprops & kAcyclic) { + VLOG(1) << "Check synchronize(T) equiv T"; + SynchronizeFst<Arc> S(T); + CHECK(Equiv(T, S)); + } + } + + // Tests search operations + void TestSearch(const Fst<Arc> &T) { + uint64 wprops = Weight::Properties(); + + VectorFst<Arc> A(T); + Project(&A, PROJECT_INPUT); + + if ((wprops & (kPath | kRightSemiring)) == (kPath | kRightSemiring)) { + VLOG(1) << "Check 1-best weight."; + VectorFst<Arc> path; + ShortestPath(T, &path); + Weight tsum = ShortestDistance(T); + Weight psum = ShortestDistance(path); + CHECK(ApproxEqual(tsum, psum, kTestDelta)); + } + + if ((wprops & (kPath | kSemiring)) == (kPath | kSemiring)) { + VLOG(1) << "Check n-best weights"; + VectorFst<Arc> R(A); + RmEpsilon(&R); + int nshortest = rand() % kNumRandomShortestPaths + 2; + VectorFst<Arc> paths; + ShortestPath(R, &paths, nshortest, true, false, + Weight::Zero(), kNumShortestStates); + vector<Weight> distance; + ShortestDistance(paths, &distance, true); + StateId pstart = paths.Start(); + if (pstart != kNoStateId) { + ArcIterator< Fst<Arc> > piter(paths, pstart); + for (; !piter.Done(); piter.Next()) { + StateId s = piter.Value().nextstate; + Weight nsum = s < distance.size() ? + Times(piter.Value().weight, distance[s]) : Weight::Zero(); + VectorFst<Arc> path; + ShortestPath(R, &path); + Weight dsum = ShortestDistance(path); + CHECK(ApproxEqual(nsum, dsum, kTestDelta)); + ArcMap(&path, RmWeightMapper<Arc>()); + VectorFst<Arc> S; + Difference(R, path, &S); + R = S; + } + } + } + } + + // Tests if two FSTS are equivalent by checking if random + // strings from one FST are transduced the same by both FSTs. + bool Equiv(const Fst<Arc> &fst1, const Fst<Arc> &fst2) { + VLOG(1) << "Check FSTs for sanity (including property bits)."; + CHECK(Verify(fst1)); + CHECK(Verify(fst2)); + + UniformArcSelector<Arc> uniform_selector(seed_); + RandGenOptions< UniformArcSelector<Arc> > + opts(uniform_selector, kRandomPathLength); + return RandEquivalent(fst1, fst2, kNumRandomPaths, kTestDelta, opts); + } + + // Random seed + int seed_; + + // FST with no states + VectorFst<Arc> zero_fst_; + + // FST with one state that accepts epsilon. + VectorFst<Arc> one_fst_; + + // FST with one state that accepts all strings. + VectorFst<Arc> univ_fst_; + + // Generates weights used in testing. + WeightGenerator *weight_generator_; + + // Maximum random path length. + static const int kRandomPathLength; + + // Number of random paths to explore. + static const int kNumRandomPaths; + + // Maximum number of nshortest paths. + static const int kNumRandomShortestPaths; + + // Maximum number of nshortest states. + static const int kNumShortestStates; + + // Delta for equivalence tests. + static const float kTestDelta; + + DISALLOW_COPY_AND_ASSIGN(WeightedTester); +}; + + +template <class A, class WG> +const int WeightedTester<A, WG>::kRandomPathLength = 25; + +template <class A, class WG> +const int WeightedTester<A, WG>::kNumRandomPaths = 100; + +template <class A, class WG> +const int WeightedTester<A, WG>::kNumRandomShortestPaths = 100; + +template <class A, class WG> +const int WeightedTester<A, WG>::kNumShortestStates = 10000; + +template <class A, class WG> +const float WeightedTester<A, WG>::kTestDelta = .05; + +// This class tests a variety of identities and properties that must +// hold for various algorithms on unweighted FSAs and that are not tested +// by WeightedTester. Only the specialization does anything interesting. +template <class Arc> +class UnweightedTester { + public: + UnweightedTester(const Fst<Arc> &zero_fsa, const Fst<Arc> &one_fsa, + const Fst<Arc> &univ_fsa) {} + + void Test(const Fst<Arc> &A1, const Fst<Arc> &A2, const Fst<Arc> &A3) {} +}; + + +// Specialization for StdArc. This should work for any commutative, +// idempotent semiring when restricted to the unweighted case +// (being isomorphic to the boolean semiring). +template <> +class UnweightedTester<StdArc> { + public: + typedef StdArc Arc; + typedef Arc::Label Label; + typedef Arc::StateId StateId; + typedef Arc::Weight Weight; + + UnweightedTester(const Fst<Arc> &zero_fsa, const Fst<Arc> &one_fsa, + const Fst<Arc> &univ_fsa) + : zero_fsa_(zero_fsa), one_fsa_(one_fsa), univ_fsa_(univ_fsa) {} + + void Test(const Fst<Arc> &A1, const Fst<Arc> &A2, const Fst<Arc> &A3) { + TestRational(A1, A2, A3); + TestIntersect(A1, A2, A3); + TestOptimize(A1); + } + + private: + // Tests rational operations with identities + void TestRational(const Fst<Arc> &A1, const Fst<Arc> &A2, + const Fst<Arc> &A3) { + + { + VLOG(1) << "Check the union contains its arguments (destructive)."; + VectorFst<Arc> U(A1); + Union(&U, A2); + + CHECK(Subset(A1, U)); + CHECK(Subset(A2, U)); + } + + { + VLOG(1) << "Check the union contains its arguments (delayed)."; + UnionFst<Arc> U(A1, A2); + + CHECK(Subset(A1, U)); + CHECK(Subset(A2, U)); + } + + { + VLOG(1) << "Check if A^n c A* (destructive)."; + VectorFst<Arc> C(one_fsa_); + int n = rand() % 5; + for (int i = 0; i < n; ++i) + Concat(&C, A1); + + VectorFst<Arc> S(A1); + Closure(&S, CLOSURE_STAR); + CHECK(Subset(C, S)); + } + + { + VLOG(1) << "Check if A^n c A* (delayed)."; + int n = rand() % 5; + Fst<Arc> *C = new VectorFst<Arc>(one_fsa_); + for (int i = 0; i < n; ++i) { + ConcatFst<Arc> *F = new ConcatFst<Arc>(*C, A1); + delete C; + C = F; + } + ClosureFst<Arc> S(A1, CLOSURE_STAR); + CHECK(Subset(*C, S)); + delete C; + } + } + + // Tests intersect-based operations. + void TestIntersect(const Fst<Arc> &A1, const Fst<Arc> &A2, + const Fst<Arc> &A3) { + VectorFst<Arc> S1(A1); + VectorFst<Arc> S2(A2); + VectorFst<Arc> S3(A3); + + ILabelCompare<Arc> comp; + + ArcSort(&S1, comp); + ArcSort(&S2, comp); + ArcSort(&S3, comp); + + { + VLOG(1) << "Check the intersection is contained in its arguments."; + IntersectFst<Arc> I1(S1, S2); + CHECK(Subset(I1, S1)); + CHECK(Subset(I1, S2)); + } + + { + VLOG(1) << "Check union distributes over intersection."; + IntersectFst<Arc> I1(S1, S2); + UnionFst<Arc> U1(I1, S3); + + UnionFst<Arc> U2(S1, S3); + UnionFst<Arc> U3(S2, S3); + ArcSortFst< Arc, ILabelCompare<Arc> > S4(U3, comp); + IntersectFst<Arc> I2(U2, S4); + + CHECK(Equiv(U1, I2)); + } + + VectorFst<Arc> C1; + VectorFst<Arc> C2; + Complement(S1, &C1); + Complement(S2, &C2); + ArcSort(&C1, comp); + ArcSort(&C2, comp); + + + { + VLOG(1) << "Check S U S' = Sigma*"; + UnionFst<Arc> U(S1, C1); + CHECK(Equiv(U, univ_fsa_)); + } + + { + VLOG(1) << "Check S n S' = {}"; + IntersectFst<Arc> I(S1, C1); + CHECK(Equiv(I, zero_fsa_)); + } + + { + VLOG(1) << "Check (S1' U S2') == (S1 n S2)'"; + UnionFst<Arc> U(C1, C2); + + IntersectFst<Arc> I(S1, S2); + VectorFst<Arc> C3; + Complement(I, &C3); + CHECK(Equiv(U, C3)); + } + + { + VLOG(1) << "Check (S1' n S2') == (S1 U S2)'"; + IntersectFst<Arc> I(C1, C2); + + UnionFst<Arc> U(S1, S2); + VectorFst<Arc> C3; + Complement(U, &C3); + CHECK(Equiv(I, C3)); + } + } + + // Tests optimization operations + void TestOptimize(const Fst<Arc> &A) { + { + VLOG(1) << "Check determinized FSA is equivalent to its input."; + DeterminizeFst<Arc> D(A); + CHECK(Equiv(A, D)); + } + + { + VLOG(1) << "Check minimized FSA is equivalent to its input."; + int n; + { + RmEpsilonFst<Arc> R(A); + DeterminizeFst<Arc> D(R); + VectorFst<Arc> M(D); + Minimize(&M); + CHECK(Equiv(A, M)); + n = M.NumStates(); + } + + if (n) { // Skip test if A is the empty machine + VLOG(1) << "Check that Hopcroft's and Revuz's algorithms lead to the" + << " same number of states as Brozozowski's algorithm"; + VectorFst<Arc> R; + Reverse(A, &R); + RmEpsilon(&R); + DeterminizeFst<Arc> DR(R); + VectorFst<Arc> RD; + Reverse(DR, &RD); + DeterminizeFst<Arc> DRD(RD); + VectorFst<Arc> M(DRD); + CHECK_EQ(n + 1, M.NumStates()); // Accounts for the epsilon transition + // to the initial state + } + } + } + + // Tests if two FSAS are equivalent. + bool Equiv(const Fst<Arc> &fsa1, const Fst<Arc> &fsa2) { + VLOG(1) << "Check FSAs for sanity (including property bits)."; + CHECK(Verify(fsa1)); + CHECK(Verify(fsa2)); + + VectorFst<Arc> vfsa1(fsa1); + VectorFst<Arc> vfsa2(fsa2); + RmEpsilon(&vfsa1); + RmEpsilon(&vfsa2); + DeterminizeFst<Arc> dfa1(vfsa1); + DeterminizeFst<Arc> dfa2(vfsa2); + + // Test equivalence using union-find algorithm + bool equiv1 = Equivalent(dfa1, dfa2); + + // Test equivalence by checking if (S1 - S2) U (S2 - S1) is empty + ILabelCompare<Arc> comp; + VectorFst<Arc> sdfa1(dfa1); + ArcSort(&sdfa1, comp); + VectorFst<Arc> sdfa2(dfa2); + ArcSort(&sdfa2, comp); + + DifferenceFst<Arc> dfsa1(sdfa1, sdfa2); + DifferenceFst<Arc> dfsa2(sdfa2, sdfa1); + + VectorFst<Arc> ufsa(dfsa1); + Union(&ufsa, dfsa2); + Connect(&ufsa); + bool equiv2 = ufsa.NumStates() == 0; + + // Check two equivalence tests match + CHECK((equiv1 && equiv2) || (!equiv1 && !equiv2)); + + return equiv1; + } + + // Tests if FSA1 is a subset of FSA2 (disregarding weights). + bool Subset(const Fst<Arc> &fsa1, const Fst<Arc> &fsa2) { + VLOG(1) << "Check FSAs (incl. property bits) for sanity"; + CHECK(Verify(fsa1)); + CHECK(Verify(fsa2)); + + VectorFst<StdArc> vfsa1; + VectorFst<StdArc> vfsa2; + RmEpsilon(&vfsa1); + RmEpsilon(&vfsa2); + ILabelCompare<StdArc> comp; + ArcSort(&vfsa1, comp); + ArcSort(&vfsa2, comp); + IntersectFst<StdArc> ifsa(vfsa1, vfsa2); + DeterminizeFst<StdArc> dfa1(vfsa1); + DeterminizeFst<StdArc> dfa2(ifsa); + return Equivalent(dfa1, dfa2); + } + + // Returns complement Fsa + void Complement(const Fst<Arc> &ifsa, MutableFst<Arc> *ofsa) { + RmEpsilonFst<Arc> rfsa(ifsa); + DeterminizeFst<Arc> dfa(rfsa); + DifferenceFst<Arc> cfsa(univ_fsa_, dfa); + *ofsa = cfsa; + } + + // FSA with no states + VectorFst<Arc> zero_fsa_; + + // FSA with one state that accepts epsilon. + VectorFst<Arc> one_fsa_; + + // FSA with one state that accepts all strings. + VectorFst<Arc> univ_fsa_; + + DISALLOW_COPY_AND_ASSIGN(UnweightedTester); +}; + + +// This class tests a variety of identities and properties that must +// hold for various FST algorithms. It randomly generates FSTs, using +// function object 'weight_generator' to select weights. 'WeightTester' +// and 'UnweightedTester' are then called. +template <class Arc, class WeightGenerator> +class AlgoTester { + public: + typedef typename Arc::Label Label; + typedef typename Arc::StateId StateId; + typedef typename Arc::Weight Weight; + + AlgoTester(WeightGenerator generator, int seed) : + weight_generator_(generator), seed_(seed) { + one_fst_.AddState(); + one_fst_.SetStart(0); + one_fst_.SetFinal(0, Weight::One()); + + univ_fst_.AddState(); + univ_fst_.SetStart(0); + univ_fst_.SetFinal(0, Weight::One()); + for (int i = 0; i < kNumRandomLabels; ++i) + univ_fst_.AddArc(0, Arc(i, i, Weight::One(), 0)); + } + + void Test() { + VLOG(1) << "weight type = " << Weight::Type(); + + for (int i = 0; i < FLAGS_repeat; ++i) { + // Random transducers + VectorFst<Arc> T1; + VectorFst<Arc> T2; + VectorFst<Arc> T3; + RandFst(&T1); + RandFst(&T2); + RandFst(&T3); + WeightedTester<Arc, WeightGenerator> + weighted_tester(seed_, zero_fst_, one_fst_, + univ_fst_, &weight_generator_); + weighted_tester.Test(T1, T2, T3); + + VectorFst<Arc> A1(T1); + VectorFst<Arc> A2(T2); + VectorFst<Arc> A3(T3); + Project(&A1, PROJECT_OUTPUT); + Project(&A2, PROJECT_INPUT); + Project(&A3, PROJECT_INPUT); + ArcMap(&A1, rm_weight_mapper); + ArcMap(&A2, rm_weight_mapper); + ArcMap(&A3, rm_weight_mapper); + UnweightedTester<Arc> unweighted_tester(zero_fst_, one_fst_, univ_fst_); + unweighted_tester.Test(A1, A2, A3); + } + } + + private: + // Generates a random FST. + void RandFst(MutableFst<Arc> *fst) { + // Determines direction of the arcs wrt state numbering. This way we + // can force acyclicity when desired. + enum ArcDirection { ANY_DIRECTION = 0, FORWARD_DIRECTION = 1, + REVERSE_DIRECTION = 2, NUM_DIRECTIONS = 3 }; + + ArcDirection arc_direction = ANY_DIRECTION; + if (rand()/(RAND_MAX + 1.0) < kAcyclicProb) + arc_direction = rand() % 2 ? FORWARD_DIRECTION : REVERSE_DIRECTION; + + fst->DeleteStates(); + StateId ns = rand() % kNumRandomStates; + + if (ns == 0) + return; + for (StateId s = 0; s < ns; ++s) + fst->AddState(); + + StateId start = rand() % ns; + fst->SetStart(start); + + size_t na = rand() % kNumRandomArcs; + for (size_t n = 0; n < na; ++n) { + StateId s = rand() % ns; + Arc arc; + arc.ilabel = rand() % kNumRandomLabels; + arc.olabel = rand() % kNumRandomLabels; + arc.weight = weight_generator_(); + arc.nextstate = rand() % ns; + + if (arc_direction == ANY_DIRECTION || + (arc_direction == FORWARD_DIRECTION && arc.ilabel > arc.olabel) || + (arc_direction == REVERSE_DIRECTION && arc.ilabel < arc.olabel)) + fst->AddArc(s, arc); + } + + StateId nf = rand() % (ns + 1); + for (StateId n = 0; n < nf; ++n) { + StateId s = rand() % ns; + Weight final = weight_generator_(); + fst->SetFinal(s, final); + } + VLOG(1) << "Check FST for sanity (including property bits)."; + CHECK(Verify(*fst)); + + // Get/compute all properties. + uint64 props = fst->Properties(kFstProperties, true); + + // Select random set of properties to be unknown. + uint64 mask = 0; + for (int n = 0; n < 8; ++n) { + mask |= rand() & 0xff; + mask <<= 8; + } + mask &= ~kTrinaryProperties; + fst->SetProperties(props & ~mask, mask); + } + + // Generates weights used in testing. + WeightGenerator weight_generator_; + + // Random seed + int seed_; + + // FST with no states + VectorFst<Arc> zero_fst_; + + // FST with one state that accepts epsilon. + VectorFst<Arc> one_fst_; + + // FST with one state that accepts all strings. + VectorFst<Arc> univ_fst_; + + // Mapper to remove weights from an Fst + RmWeightMapper<Arc> rm_weight_mapper; + + // Maximum number of states in random test Fst. + static const int kNumRandomStates; + + // Maximum number of arcs in random test Fst. + static const int kNumRandomArcs; + + // Number of alternative random labels. + static const int kNumRandomLabels; + + // Probability to force an acyclic Fst + static const float kAcyclicProb; + + // Maximum random path length. + static const int kRandomPathLength; + + // Number of random paths to explore. + static const int kNumRandomPaths; + + DISALLOW_COPY_AND_ASSIGN(AlgoTester); +}; + +template <class A, class G> const int AlgoTester<A, G>::kNumRandomStates = 10; + +template <class A, class G> const int AlgoTester<A, G>::kNumRandomArcs = 25; + +template <class A, class G> const int AlgoTester<A, G>::kNumRandomLabels = 5; + +template <class A, class G> const float AlgoTester<A, G>::kAcyclicProb = .25; + +template <class A, class G> const int AlgoTester<A, G>::kRandomPathLength = 25; + +template <class A, class G> const int AlgoTester<A, G>::kNumRandomPaths = 100; + +} // namespace fst + +#endif // FST_TEST_ALGO_TEST_H__ diff --git a/src/test/fst_test.cc b/src/test/fst_test.cc new file mode 100644 index 0000000..7d8d600 --- /dev/null +++ b/src/test/fst_test.cc @@ -0,0 +1,228 @@ +// fst_test.cc + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Copyright 2005-2010 Google, Inc. +// Author: riley@google.com (Michael Riley) +// +// \file +// Regression test for FST classes. + +#include "./fst_test.h" + +#include <fst/compact-fst.h> +#include <fst/const-fst.h> +#include <fst/edit-fst.h> +#include <fst/matcher-fst.h> + + +namespace fst { + +// A user-defined arc type. +struct CustomArc { + typedef short Label; + typedef ProductWeight<TropicalWeight, LogWeight> Weight; + typedef int64 StateId; + + CustomArc(Label i, Label o, Weight w, StateId s) : + ilabel(i), olabel(o), weight(w), nextstate(s) {} + CustomArc() {} + + static const string &Type() { // Arc type name + static const string type = "my"; + return type; + } + + Label ilabel; // Transition input label + Label olabel; // Transition output label + Weight weight; // Transition weight + StateId nextstate; // Transition destination state +}; + + +// A user-defined compactor for test FST. +template <class A> +class CustomCompactor { + public: + typedef A Arc; + typedef typename A::Label Label; + typedef typename A::StateId StateId; + typedef typename A::Weight Weight; + typedef pair<Label, Weight> Element; + + Element Compact(StateId s, const A &arc) const { + return make_pair(arc.ilabel, arc.weight); + } + + Arc Expand(StateId s, const Element &p, uint32 f = kArcValueFlags) const { + return p.first == kNoLabel ? + Arc(kNoLabel, kNoLabel, p.second, kNoStateId) : + Arc(p.first, 0, p.second, s); + } + + ssize_t Size() const { return -1;} + + uint64 Properties() const { return 0ULL;} + + bool Compatible(const Fst<A> &fst) const { + return true; + } + + static const string &Type() { + static const string type = "my"; + return type; + } + + bool Write(ostream &strm) const { return true; } + + static CustomCompactor *Read(istream &strm) { + return new CustomCompactor; + } +}; + + +REGISTER_FST(VectorFst, CustomArc); +REGISTER_FST(ConstFst, CustomArc); +static fst::FstRegisterer< + CompactFst<StdArc, CustomCompactor<StdArc> > > +CompactFst_StdArc_CustomCompactor_registerer; +static fst::FstRegisterer< + CompactFst<CustomArc, CustomCompactor<CustomArc> > > +CompactFst_CustomArc_CustomCompactor_registerer; +static fst::FstRegisterer<ConstFst<StdArc, uint16> > +ConstFst_StdArc_uint16_registerer; +static fst::FstRegisterer< + CompactFst<StdArc, CustomCompactor<StdArc>, uint16> > +CompactFst_StdArc_CustomCompactor_uint16_registerer; + +} // namespace fst + + +using fst::FstTester; +using fst::VectorFst; +using fst::ConstFst; +using fst::MatcherFst; +using fst::CompactFst; +using fst::Fst; +using fst::StdArc; +using fst::CustomArc; +using fst::CustomCompactor; +using fst::StdArcLookAheadFst; +using fst::EditFst; + +int main(int argc, char **argv) { + FLAGS_fst_verify_properties = true; + std::set_new_handler(FailedNewHandler); + SetFlags(argv[0], &argc, &argv, true); + + // VectorFst<StdArc> tests + { + FstTester< VectorFst<StdArc> > std_vector_tester; + std_vector_tester.TestBase(); + std_vector_tester.TestExpanded(); + std_vector_tester.TestAssign(); + std_vector_tester.TestCopy(); + std_vector_tester.TestIO(); + std_vector_tester.TestMutable(); + } + + // ConstFst<StdArc> tests + { + FstTester< ConstFst<StdArc> > std_const_tester; + std_const_tester.TestBase(); + std_const_tester.TestExpanded(); + std_const_tester.TestCopy(); + std_const_tester.TestIO(); + } + + // CompactFst<StdArc, CustomCompactor<StdArc> > + { + FstTester< CompactFst<StdArc, CustomCompactor<StdArc> > > + std_compact_tester; + std_compact_tester.TestBase(); + std_compact_tester.TestExpanded(); + std_compact_tester.TestCopy(); + std_compact_tester.TestIO(); + } + + // VectorFst<CustomArc> tests + { + FstTester< VectorFst<CustomArc> > std_vector_tester; + std_vector_tester.TestBase(); + std_vector_tester.TestExpanded(); + std_vector_tester.TestAssign(); + std_vector_tester.TestCopy(); + std_vector_tester.TestIO(); + std_vector_tester.TestMutable(); + } + + // ConstFst<CustomArc> tests + { + FstTester< ConstFst<CustomArc> > std_const_tester; + std_const_tester.TestBase(); + std_const_tester.TestExpanded(); + std_const_tester.TestCopy(); + std_const_tester.TestIO(); + } + + // CompactFst<CustomArc, CustomCompactor<CustomArc> > + { + FstTester< CompactFst<CustomArc, CustomCompactor<CustomArc> > > + std_compact_tester; + std_compact_tester.TestBase(); + std_compact_tester.TestExpanded(); + std_compact_tester.TestCopy(); + std_compact_tester.TestIO(); + } + + // ConstFst<StdArc, uint16> tests + { + FstTester< ConstFst<StdArc, uint16> > std_const_tester; + std_const_tester.TestBase(); + std_const_tester.TestExpanded(); + std_const_tester.TestCopy(); + std_const_tester.TestIO(); + } + + // CompactFst<StdArc, CustomCompactor<StdArc>, uint16> + { + FstTester< CompactFst<StdArc, CustomCompactor<StdArc>, uint16> > + std_compact_tester; + std_compact_tester.TestBase(); + std_compact_tester.TestExpanded(); + std_compact_tester.TestCopy(); + std_compact_tester.TestIO(); + } + + // FstTester<StdArcLookAheadFst> + { + FstTester<StdArcLookAheadFst> std_matcher_tester; + std_matcher_tester.TestBase(); + std_matcher_tester.TestExpanded(); + std_matcher_tester.TestCopy(); + } + + // EditFst<StdArc> tests + { + FstTester< EditFst<StdArc> > std_edit_tester; + std_edit_tester.TestBase(); + std_edit_tester.TestExpanded(); + std_edit_tester.TestAssign(); + std_edit_tester.TestCopy(); + std_edit_tester.TestMutable(); + } + + cout << "PASS" << endl; + + return 0; +} diff --git a/src/test/fst_test.h b/src/test/fst_test.h new file mode 100644 index 0000000..da16476 --- /dev/null +++ b/src/test/fst_test.h @@ -0,0 +1,299 @@ +// fst_test.h + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Copyright 2005-2010 Google, Inc. +// Author: riley@google.com (Michael Riley) +// +// \file +// Regression test for FST classes. + +#ifndef FST_TEST_FST_TEST_H_ +#define FST_TEST_FST_TEST_H_ + +#include <fst/equal.h> +#include <fst/matcher.h> +#include <fst/vector-fst.h> +#include <fst/verify.h> + +DECLARE_string(tmpdir); + +namespace fst { + +// This tests an Fst F that is assumed to have a copy method from an +// arbitrary Fst. Some test functions make further assumptions mostly +// obvious from their name. These tests are written as member temple +// functions that take a test fst as its argument so that different +// Fsts in the interface hierarchy can be tested separately and so +// that we can instantiate only those tests that make sense for a +// particular Fst. +template <class F> +class FstTester { + public: + typedef typename F::Arc Arc; + typedef typename Arc::StateId StateId; + typedef typename Arc::Weight Weight; + typedef typename Arc::Label Label; + + FstTester() { + VectorFst<Arc> vfst; + InitFst(&vfst, 128); + testfst_ = new F(vfst); + } + + ~FstTester() { + delete testfst_; + } + + // This verifies the contents described in InitFst() using + // methods defined in a generic Fst. + template <class G> + void TestBase(const G &fst) const { + CHECK(Verify(fst)); + CHECK_EQ(fst.Start(), 0); + StateId ns = 0; + StateIterator<G> siter(fst); + Matcher<G> matcher(fst, MATCH_INPUT); + MatchType match_type = matcher.Type(true); + for (; !siter.Done(); siter.Next()); + for (siter.Reset(); !siter.Done(); siter.Next()) { + StateId s = siter.Value(); + matcher.SetState(s); + CHECK_EQ(fst.Final(s), NthWeight(s)); + size_t na = 0; + ArcIterator<G> aiter(fst, s); + for (; !aiter.Done(); aiter.Next()); + for (aiter.Reset(); !aiter.Done(); aiter.Next()) { + ++na; + const Arc &arc = aiter.Value(); + CHECK_EQ(arc.ilabel, na); + CHECK_EQ(arc.olabel, 0); + CHECK_EQ(arc.weight, NthWeight(na)); + CHECK_EQ(arc.nextstate, s); + if (match_type == MATCH_INPUT) { + CHECK(matcher.Find(arc.ilabel)); + CHECK_EQ(matcher.Value().ilabel, arc.ilabel); + } + } + CHECK_EQ(na, s); + CHECK_EQ(na, aiter.Position()); + CHECK_EQ(fst.NumArcs(s), s); + CHECK_EQ(fst.NumInputEpsilons(s), 0); + CHECK_EQ(fst.NumOutputEpsilons(s), s); + CHECK(!matcher.Find(s + 1)); // out-of-range + CHECK(!matcher.Find(kNoLabel)); // no explicit epsilons + CHECK(matcher.Find(0)); + CHECK_EQ(matcher.Value().ilabel, kNoLabel); // implicit epsilon loop + ++ns; + } + CHECK(fst.Properties(kNotAcceptor, true)); + CHECK(fst.Properties(kOEpsilons, true)); + } + + void TestBase() const { + TestBase(*testfst_); + } + + // This verifies methods specfic to an ExpandedFst. + template <class G> + void TestExpanded(const G &fst) const { + StateId ns = 0; + for (StateIterator<G> siter(fst); + !siter.Done(); + siter.Next()) { + ++ns; + } + CHECK_EQ(fst.NumStates(), ns); + CHECK(fst.Properties(kExpanded, false)); + } + + void TestExpanded() const { TestExpanded(*testfst_); } + + // This verifies methods specific to a MutableFst. + template <class G> + void TestMutable(G *fst) const { + for (StateIterator<G> siter(*fst); + !siter.Done(); + siter.Next()) { + StateId s = siter.Value(); + size_t na = 0; + size_t ni = fst->NumInputEpsilons(s); + MutableArcIterator<G> aiter(fst, s); + for (; !aiter.Done(); aiter.Next()); + for (aiter.Reset(); !aiter.Done(); aiter.Next()) { + ++na; + Arc arc = aiter.Value(); + arc.ilabel = 0; + aiter.SetValue(arc); + arc = aiter.Value(); + CHECK_EQ(arc.ilabel, 0); + CHECK_EQ(fst->NumInputEpsilons(s), ni + 1); + arc.ilabel = na; + aiter.SetValue(arc); + CHECK_EQ(fst->NumInputEpsilons(s), ni); + } + } + + G *cfst1 = fst->Copy(); + cfst1->DeleteStates(); + CHECK_EQ(cfst1->NumStates(), 0); + delete cfst1; + + G *cfst2 = fst->Copy(); + for (StateIterator<G> siter(*cfst2); + !siter.Done(); + siter.Next()) { + StateId s = siter.Value(); + cfst2->DeleteArcs(s); + CHECK_EQ(cfst2->NumArcs(s), 0); + CHECK_EQ(cfst2->NumInputEpsilons(s), 0); + CHECK_EQ(cfst2->NumOutputEpsilons(s), 0); + } + delete cfst2; + } + + void TestMutable() { TestMutable(testfst_); } + + // This verifies the copy methods. + template <class G> + void TestAssign(G *fst) const { + // Assignment from G + G afst1; + afst1 = *fst; + CHECK(Equal(*fst, afst1)); + + // Assignment from Fst + G afst2; + afst2 = *static_cast<const Fst<Arc> *>(fst); + CHECK(Equal(*fst, afst2)); + + // Assignment from self + afst2.operator=(afst2); + CHECK(Equal(*fst, afst2)); + } + + void TestAssign() { TestAssign(testfst_); } + + // This verifies the copy methods. + template <class G> + void TestCopy(const G &fst) const { + // Copy from G + G c1fst(fst); + TestBase(c1fst); + + // Copy from Fst + const G c2fst(static_cast<const Fst<Arc> &>(fst)); + TestBase(c2fst); + + // Copy from self + const G *c3fst = fst.Copy(); + TestBase(*c3fst); + delete c3fst; + } + + void TestCopy() const { TestCopy(*testfst_); } + + // This verifies the read/write methods. + template <class G> + void TestIO(const G &fst) const { + const string filename = FLAGS_tmpdir + "/test.fst"; + { + // write/read + CHECK(fst.Write(filename)); + G *ffst = G::Read(filename); + CHECK(ffst); + TestBase(*ffst); + delete ffst; + } + + { + // generic read/cast/test + Fst<Arc> *gfst = Fst<Arc>::Read(filename); + CHECK(gfst); + G *dfst = static_cast<G *>(gfst); + TestBase(*dfst); + + // generic write/read/test + CHECK(gfst->Write(filename)); + Fst<Arc> *hfst = Fst<Arc>::Read(filename); + CHECK(hfst); + TestBase(*hfst); + delete gfst; + delete hfst; + } + + // expanded write/read/test + if (fst.Properties(kExpanded, false)) { + ExpandedFst<Arc> *efst = ExpandedFst<Arc>::Read(filename); + CHECK(efst); + TestBase(*efst); + TestExpanded(*efst); + delete efst; + } + + // mutable write/read/test + if (fst.Properties(kMutable, false)) { + MutableFst<Arc> *mfst = MutableFst<Arc>::Read(filename); + CHECK(mfst); + TestBase(*mfst); + TestExpanded(*mfst); + TestMutable(mfst); + delete mfst; + } + } + + void TestIO() const { TestIO(*testfst_); } + + private: + // This constructs test FSTs. Given a mutable FST, will leave + // the FST as follows: + // (I) NumStates() = nstates + // (II) Start() = 0 + // (III) Final(s) = NthWeight(s) + // (IV) For state s: + // (a) NumArcs(s) == s + // (b) For ith arc of s: + // (1) ilabel = i + // (2) olabel = 0 + // (3) weight = NthWeight(i) + // (4) nextstate = s + void InitFst(MutableFst<Arc> *fst, size_t nstates) const { + fst->DeleteStates(); + CHECK_GT(nstates, 0); + + for (StateId s = 0; s < nstates; ++s) { + fst->AddState(); + fst->SetFinal(s, NthWeight(s)); + for (size_t i = 1; i <= s; ++i) { + Arc arc(i, 0, NthWeight(i), s); + fst->AddArc(s, arc); + } + } + + fst->SetStart(0); + } + + // Generates One() + ... + One() (n times) + Weight NthWeight(int n) const { + Weight w = Weight::Zero(); + for (int i = 0; i < n; ++i) + w = Plus(w, Weight::One()); + return w; + } + + F *testfst_; // what we're testing +}; + +} // namespace fst + +#endif // FST_TEST_FST_TEST_H_ diff --git a/src/test/weight-tester.h b/src/test/weight-tester.h new file mode 100644 index 0000000..751e7d6 --- /dev/null +++ b/src/test/weight-tester.h @@ -0,0 +1,225 @@ +// weight-tester.h + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Copyright 2005-2010 Google, Inc. +// Author: riley@google.com (Michael Riley) +// +// \file +// Utility class for regression testing of Fst weights. + +#ifndef FST_TEST_WEIGHT_TESTER_H_ +#define FST_TEST_WEIGHT_TESTER_H_ + +#include <iostream> +#include <sstream> + +#include <fst/random-weight.h> + +namespace fst { + +// This class tests a variety of identities and properties that must +// hold for the Weight class to be well-defined. It calls function object +// WEIGHT_GENERATOR to select weights that are used in the tests. +template<class Weight, class WeightGenerator> +class WeightTester { + public: + WeightTester(WeightGenerator generator) : weight_generator_(generator) {} + + void Test(int iterations, bool test_division = true) { + for (int i = 0; i < iterations; ++i) { + // Selects the test weights. + Weight w1 = weight_generator_(); + Weight w2 = weight_generator_(); + Weight w3 = weight_generator_(); + + VLOG(1) << "weight type = " << Weight::Type(); + VLOG(1) << "w1 = " << w1; + VLOG(1) << "w2 = " << w2; + VLOG(1) << "w3 = " << w3; + + TestSemiring(w1, w2, w3); + if (test_division) + TestDivision(w1, w2); + TestReverse(w1, w2); + TestEquality(w1, w2, w3); + TestIO(w1); + TestCopy(w1); + } + } + + private: + // Note in the tests below we use ApproxEqual rather than == and add + // kDelta to inequalities where the weights might be inexact. + + // Tests (Plus, Times, Zero, One) defines a commutative semiring. + void TestSemiring(Weight w1, Weight w2, Weight w3) { + // Checks that the operations are closed. + CHECK(Plus(w1, w2).Member()); + CHECK(Times(w1, w2).Member()); + + // Checks that the operations are associative. + CHECK(ApproxEqual(Plus(w1, Plus(w2, w3)), Plus(Plus(w1, w2), w3))); + CHECK(ApproxEqual(Times(w1, Times(w2, w3)), Times(Times(w1, w2), w3))); + + // Checks the identity elements. + CHECK(Plus(w1, Weight::Zero()) == w1); + CHECK(Plus(Weight::Zero(), w1) == w1); + CHECK(Times(w1, Weight::One()) == w1); + CHECK(Times(Weight::One(), w1) == w1); + + // Check the no weight element. + CHECK(!Weight::NoWeight().Member()); + CHECK(!Plus(w1, Weight::NoWeight()).Member()); + CHECK(!Plus(Weight::NoWeight(), w1).Member()); + CHECK(!Times(w1, Weight::NoWeight()).Member()); + CHECK(!Times(Weight::NoWeight(), w1).Member()); + + // Checks that the operations commute. + CHECK(ApproxEqual(Plus(w1, w2), Plus(w2, w1))); + if (Weight::Properties() & kCommutative) + CHECK(ApproxEqual(Times(w1, w2), Times(w2, w1))); + + // Checks Zero() is the annihilator. + CHECK(Times(w1, Weight::Zero()) == Weight::Zero()); + CHECK(Times(Weight::Zero(), w1) == Weight::Zero()); + + // Check Power(w, 0) is Weight::One() + CHECK(Power(w1, 0) == Weight::One()); + + // Check Power(w, 1) is w + CHECK(Power(w1, 1) == w1); + + // Check Power(w, 3) is Times(w, Times(w, w)) + CHECK(Power(w1, 3) == Times(w1, Times(w1, w1))); + + // Checks distributivity. + if (Weight::Properties() & kLeftSemiring) + CHECK(ApproxEqual(Times(w1, Plus(w2, w3)), + Plus(Times(w1, w2), Times(w1, w3)))); + if (Weight::Properties() & kRightSemiring) + CHECK(ApproxEqual(Times(Plus(w1, w2), w3), + Plus(Times(w1, w3), Times(w2, w3)))); + + if (Weight::Properties() & kIdempotent) + CHECK(Plus(w1, w1) == w1); + + if (Weight::Properties() & kPath) + CHECK(Plus(w1, w2) == w1 || Plus(w1, w2) == w2); + + // Ensure weights form a left or right semiring. + CHECK(Weight::Properties() & (kLeftSemiring | kRightSemiring)); + + // Check when Times() is commutative that it is marked as a semiring. + if (Weight::Properties() & kCommutative) + CHECK(Weight::Properties() & kSemiring); + } + + // Tests division operation. + void TestDivision(Weight w1, Weight w2) { + Weight p = Times(w1, w2); + + if (Weight::Properties() & kLeftSemiring) { + Weight d = Divide(p, w1, DIVIDE_LEFT); + if (d.Member()) + CHECK(ApproxEqual(p, Times(w1, d))); + CHECK(!Divide(w1, Weight::NoWeight(), DIVIDE_LEFT).Member()); + CHECK(!Divide(Weight::NoWeight(), w1, DIVIDE_LEFT).Member()); + } + + if (Weight::Properties() & kRightSemiring) { + Weight d = Divide(p, w2, DIVIDE_RIGHT); + if (d.Member()) + CHECK(ApproxEqual(p, Times(d, w2))); + CHECK(!Divide(w1, Weight::NoWeight(), DIVIDE_RIGHT).Member()); + CHECK(!Divide(Weight::NoWeight(), w1, DIVIDE_RIGHT).Member()); + } + + if (Weight::Properties() & kCommutative) { + Weight d = Divide(p, w1, DIVIDE_RIGHT); + if (d.Member()) + CHECK(ApproxEqual(p, Times(d, w1))); + } + } + + // Tests reverse operation. + void TestReverse(Weight w1, Weight w2) { + typedef typename Weight::ReverseWeight ReverseWeight; + + ReverseWeight rw1 = w1.Reverse(); + ReverseWeight rw2 = w2.Reverse(); + + CHECK(rw1.Reverse() == w1); + CHECK(Plus(w1, w2).Reverse() == Plus(rw1, rw2)); + CHECK(Times(w1, w2).Reverse() == Times(rw2, rw1)); + } + + // Tests == is an equivalence relation. + void TestEquality(Weight w1, Weight w2, Weight w3) { + // Checks reflexivity. + CHECK(w1 == w1); + + // Checks symmetry. + CHECK((w1 == w2) == (w2 == w1)); + + // Checks transitivity. + if (w1 == w2 && w2 == w3) + CHECK(w1 == w3); + } + + // Tests binary serialization and textual I/O. + void TestIO(Weight w) { + // Tests binary I/O + { + ostringstream os; + w.Write(os); + os.flush(); + istringstream is(os.str()); + Weight v; + v.Read(is); + CHECK_EQ(w, v); + } + + // Tests textual I/O. + { + ostringstream os; + os << w; + istringstream is(os.str()); + Weight v(Weight::One()); + is >> v; + CHECK(ApproxEqual(w, v)); + } + } + + // Tests copy constructor and assignment operator + void TestCopy(Weight w) { + Weight x = w; + CHECK(w == x); + + x = Weight(w); + CHECK(w == x); + + x.operator=(x); + CHECK(w == x); + + } + + // Generates weights used in testing. + WeightGenerator weight_generator_; + + DISALLOW_COPY_AND_ASSIGN(WeightTester); +}; + +} // namespace fst + +#endif // FST_TEST_WEIGHT_TESTER_H_ diff --git a/src/test/weight_test.cc b/src/test/weight_test.cc new file mode 100644 index 0000000..54ba85d --- /dev/null +++ b/src/test/weight_test.cc @@ -0,0 +1,258 @@ +// weight_test.h + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Copyright 2005-2010 Google, Inc. +// Author: riley@google.com (Michael Riley) +// +// \file +// Regression test for Fst weights. + +#include <cstdlib> +#include <ctime> + +#include <fst/expectation-weight.h> +#include <fst/float-weight.h> +#include <fst/random-weight.h> +#include "./weight-tester.h" + +DEFINE_int32(seed, -1, "random seed"); +DEFINE_int32(repeat, 100000, "number of test repetitions"); + +using fst::TropicalWeight; +using fst::TropicalWeightGenerator; +using fst::TropicalWeightTpl; +using fst::TropicalWeightGenerator_; + +using fst::LogWeight; +using fst::LogWeightGenerator; +using fst::LogWeightTpl; +using fst::LogWeightGenerator_; + +using fst::MinMaxWeight; +using fst::MinMaxWeightGenerator; +using fst::MinMaxWeightTpl; +using fst::MinMaxWeightGenerator_; + +using fst::StringWeight; +using fst::StringWeightGenerator; + +using fst::GallicWeight; +using fst::GallicWeightGenerator; + +using fst::LexicographicWeight; +using fst::LexicographicWeightGenerator; + +using fst::ProductWeight; +using fst::ProductWeightGenerator; + +using fst::PowerWeight; +using fst::PowerWeightGenerator; + +using fst::SignedLogWeightTpl; +using fst::SignedLogWeightGenerator_; + +using fst::ExpectationWeight; + +using fst::SparsePowerWeight; +using fst::SparsePowerWeightGenerator; + +using fst::STRING_LEFT; +using fst::STRING_RIGHT; + +using fst::WeightTester; + +template <class T> +void TestTemplatedWeights(int repeat, int seed) { + TropicalWeightGenerator_<T> tropical_generator(seed); + WeightTester<TropicalWeightTpl<T>, TropicalWeightGenerator_<T> > + tropical_tester(tropical_generator); + tropical_tester.Test(repeat); + + LogWeightGenerator_<T> log_generator(seed); + WeightTester<LogWeightTpl<T>, LogWeightGenerator_<T> > + log_tester(log_generator); + log_tester.Test(repeat); + + MinMaxWeightGenerator_<T> minmax_generator(seed); + WeightTester<MinMaxWeightTpl<T>, MinMaxWeightGenerator_<T> > + minmax_tester(minmax_generator); + minmax_tester.Test(repeat); + + SignedLogWeightGenerator_<T> signedlog_generator(seed); + WeightTester<SignedLogWeightTpl<T>, SignedLogWeightGenerator_<T> > + signedlog_tester(signedlog_generator); + signedlog_tester.Test(repeat); +} + +int main(int argc, char **argv) { + std::set_new_handler(FailedNewHandler); + SetFlags(argv[0], &argc, &argv, true); + + int seed = FLAGS_seed >= 0 ? FLAGS_seed : time(0); + LOG(INFO) << "Seed = " << seed; + + TestTemplatedWeights<float>(FLAGS_repeat, seed); + TestTemplatedWeights<double>(FLAGS_repeat, seed); + FLAGS_fst_weight_parentheses = "()"; + TestTemplatedWeights<float>(FLAGS_repeat, seed); + TestTemplatedWeights<double>(FLAGS_repeat, seed); + FLAGS_fst_weight_parentheses = ""; + + // Make sure type names for templated weights are consistent + CHECK(TropicalWeight::Type() == "tropical"); + CHECK(TropicalWeightTpl<double>::Type() != TropicalWeightTpl<float>::Type()); + CHECK(LogWeight::Type() == "log"); + CHECK(LogWeightTpl<double>::Type() != LogWeightTpl<float>::Type()); + TropicalWeightTpl<double> w(15.0); + TropicalWeight tw(15.0); + + StringWeightGenerator<int> left_string_generator(seed); + WeightTester<StringWeight<int>, StringWeightGenerator<int> > + left_string_tester(left_string_generator); + left_string_tester.Test(FLAGS_repeat); + + StringWeightGenerator<int, STRING_RIGHT> right_string_generator(seed); + WeightTester<StringWeight<int, STRING_RIGHT>, + StringWeightGenerator<int, STRING_RIGHT> > + right_string_tester(right_string_generator); + right_string_tester.Test(FLAGS_repeat); + + typedef GallicWeight<int, TropicalWeight> TropicalGallicWeight; + typedef GallicWeightGenerator<int, TropicalWeightGenerator> + TropicalGallicWeightGenerator; + + TropicalGallicWeightGenerator tropical_gallic_generator(seed); + WeightTester<TropicalGallicWeight, TropicalGallicWeightGenerator> + tropical_gallic_tester(tropical_gallic_generator); + tropical_gallic_tester.Test(FLAGS_repeat); + + typedef ProductWeight<TropicalWeight, TropicalWeight> TropicalProductWeight; + typedef ProductWeightGenerator<TropicalWeightGenerator, + TropicalWeightGenerator> TropicalProductWeightGenerator; + + TropicalProductWeightGenerator tropical_product_generator(seed); + WeightTester<TropicalProductWeight, TropicalProductWeightGenerator> + tropical_product_weight_tester(tropical_product_generator); + tropical_product_weight_tester.Test(FLAGS_repeat); + + typedef PowerWeight<TropicalWeight, 3> TropicalCubeWeight; + typedef PowerWeightGenerator<TropicalWeightGenerator, 3> + TropicalCubeWeightGenerator; + + TropicalCubeWeightGenerator tropical_cube_generator(seed); + WeightTester<TropicalCubeWeight, TropicalCubeWeightGenerator> + tropical_cube_weight_tester(tropical_cube_generator); + tropical_cube_weight_tester.Test(FLAGS_repeat); + + typedef ProductWeight<TropicalWeight, TropicalProductWeight> + SecondNestedProductWeight; + typedef ProductWeightGenerator<TropicalWeightGenerator, + TropicalProductWeightGenerator> SecondNestedProductWeightGenerator; + + SecondNestedProductWeightGenerator second_nested_product_generator(seed); + WeightTester<SecondNestedProductWeight, SecondNestedProductWeightGenerator> + second_nested_product_weight_tester(second_nested_product_generator); + second_nested_product_weight_tester.Test(FLAGS_repeat); + + // This only works with fst_weight_parentheses = "()" + typedef ProductWeight<TropicalProductWeight, TropicalWeight> + FirstNestedProductWeight; + typedef ProductWeightGenerator<TropicalProductWeightGenerator, + TropicalWeightGenerator> FirstNestedProductWeightGenerator; + + FirstNestedProductWeightGenerator first_nested_product_generator(seed); + WeightTester<FirstNestedProductWeight, FirstNestedProductWeightGenerator> + first_nested_product_weight_tester(first_nested_product_generator); + + typedef PowerWeight<FirstNestedProductWeight, 3> NestedProductCubeWeight; + typedef PowerWeightGenerator<FirstNestedProductWeightGenerator, 3> + NestedProductCubeWeightGenerator; + + NestedProductCubeWeightGenerator nested_product_cube_generator(seed); + WeightTester<NestedProductCubeWeight, NestedProductCubeWeightGenerator> + nested_product_cube_weight_tester(nested_product_cube_generator); + + typedef SparsePowerWeight<NestedProductCubeWeight, + size_t > SparseNestedProductCubeWeight; + typedef SparsePowerWeightGenerator<NestedProductCubeWeightGenerator, + size_t, 3> SparseNestedProductCubeWeightGenerator; + + SparseNestedProductCubeWeightGenerator + sparse_nested_product_cube_generator(seed); + WeightTester<SparseNestedProductCubeWeight, + SparseNestedProductCubeWeightGenerator> + sparse_nested_product_cube_weight_tester( + sparse_nested_product_cube_generator); + + typedef SparsePowerWeight<LogWeight, size_t > LogSparsePowerWeight; + typedef SparsePowerWeightGenerator<LogWeightGenerator, + size_t, 3> LogSparsePowerWeightGenerator; + + LogSparsePowerWeightGenerator + log_sparse_power_weight_generator(seed); + WeightTester<LogSparsePowerWeight, + LogSparsePowerWeightGenerator> + log_sparse_power_weight_tester( + log_sparse_power_weight_generator); + + typedef ExpectationWeight<LogWeight, LogWeight> + LogLogExpectWeight; + typedef ProductWeightGenerator<LogWeightGenerator, LogWeightGenerator, + LogLogExpectWeight> LogLogExpectWeightGenerator; + + LogLogExpectWeightGenerator log_log_expect_weight_generator(seed); + WeightTester<LogLogExpectWeight, LogLogExpectWeightGenerator> + log_log_expect_weight_tester(log_log_expect_weight_generator); + + typedef ExpectationWeight<LogWeight, LogSparsePowerWeight> + LogLogSparseExpectWeight; + typedef ProductWeightGenerator< + LogWeightGenerator, + LogSparsePowerWeightGenerator, + LogLogSparseExpectWeight> LogLogSparseExpectWeightGenerator; + + LogLogSparseExpectWeightGenerator log_logsparse_expect_weight_generator(seed); + WeightTester<LogLogSparseExpectWeight, LogLogSparseExpectWeightGenerator> + log_logsparse_expect_weight_tester(log_logsparse_expect_weight_generator); + + // Test all product weight I/O with parentheses + FLAGS_fst_weight_parentheses = "()"; + first_nested_product_weight_tester.Test(FLAGS_repeat); + nested_product_cube_weight_tester.Test(FLAGS_repeat); + log_sparse_power_weight_tester.Test(1); + sparse_nested_product_cube_weight_tester.Test(1); + tropical_product_weight_tester.Test(5); + second_nested_product_weight_tester.Test(5); + tropical_gallic_tester.Test(5); + tropical_cube_weight_tester.Test(5); + FLAGS_fst_weight_parentheses = ""; + log_sparse_power_weight_tester.Test(1); + log_log_expect_weight_tester.Test(1, false); // disables division + log_logsparse_expect_weight_tester.Test(1, false); + + typedef LexicographicWeight<TropicalWeight, TropicalWeight> + TropicalLexicographicWeight; + typedef LexicographicWeightGenerator<TropicalWeightGenerator, + TropicalWeightGenerator> TropicalLexicographicWeightGenerator; + + TropicalLexicographicWeightGenerator tropical_lexicographic_generator(seed); + WeightTester<TropicalLexicographicWeight, + TropicalLexicographicWeightGenerator> + tropical_lexicographic_tester(tropical_lexicographic_generator); + tropical_lexicographic_tester.Test(FLAGS_repeat); + + cout << "PASS" << endl; + + return 0; +} |