Skip to content

Commit

Permalink
Add python bindings (#69)
Browse files Browse the repository at this point in the history
Co-authored-by: Daniel Mann <dmann@apptek.com>
Co-authored-by: Eugen Beck <ebeck@apptek.com>
  • Loading branch information
3 people authored Jul 2, 2024
1 parent a942e39 commit f4ed97e
Show file tree
Hide file tree
Showing 21 changed files with 381 additions and 23 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
*.pyc
*.o
*.so
/RASR.egg-info

# For exec-files, the postfix is ".$(OS)-$(PROC)-$(COMPILE)[-$(SUFFIX)]".
*.*-*-*
Expand Down Expand Up @@ -53,6 +54,9 @@
.history*
*.autosave

# Singularity/Apptainer images
*.sif

# Broken NFS.
.nfs*

1 change: 1 addition & 0 deletions Modules.make
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ TOOLS += CorpusStatistics
TOOLS += FeatureExtraction
TOOLS += FeatureStatistics
TOOLS += Fsa
TOOLS += LibRASR
TOOLS += Lm
TOOLS += SpeechRecognizer
TOOLS += Xml
Expand Down
2 changes: 2 additions & 0 deletions apptainer/2023-11-08_tensorflow-2.14_v1/image.def
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ Stage: build
DEBIAN_FRONTEND=noninteractive apt install -y wget git unzip gzip libssl-dev lsb-release zsh \
bison libxml2-dev libopenblas-dev libsndfile1-dev libcrypto++-dev libcppunit-dev \
parallel xmlstarlet python3-lxml htop strace gdb sox python3-pip cmake ffmpeg vim

pip3 install pybind11==2.11.1

# download the cache manager and place in /usr/local
cd /usr/local
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ MODULES += MODULE_TEST
# Tensorflow integration
MODULES += MODULE_TENSORFLOW

# ONNX integration
# MODULES += MODULE_ONNX

# define variables for the makefiles
$(foreach module, $(MODULES), $(eval $(module) = 1))

Expand All @@ -120,6 +123,8 @@ TOOLS += CorpusStatistics
TOOLS += FeatureExtraction
TOOLS += FeatureStatistics
TOOLS += Fsa
TOOLS += LibRASR
TOOLS += Lm
TOOLS += SpeechRecognizer
TOOLS += Xml

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ CXX_MINOR = $(shell $(CXX) --version | head -n 1 | sed -e 's/.*[ \t]\([0-9]\)\.\
# -----------------------------------------------------------------------------
# compiler options
DEFINES += -D_GNU_SOURCE
CCFLAGS += -fPIC
CCFLAGS += -pipe
CCFLAGS += -funsigned-char
CCFLAGS += -fno-exceptions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ LDFLAGS += `${PYTHON_PATH}/bin/python3.11-config --ldflags --embed 2>/dev/nu
LDFLAGS += -Wl,-rpath -Wl,${PYTHON_PATH}/lib
else
INCLUDES += `python3.11-config --includes 2>/dev/null`
INCLUDES += -I$(shell python3.11 -c 'import numpy as np; print(np.get_include())')
INCLUDES += `python3.11 -m pybind11 --includes 2>/dev/null`
LDFLAGS += `python3.11-config --ldflags --embed 2>/dev/null`
# IF you want to use Python2 for whatever reason:
# INCLUDES += `pkg-config --cflags python`
Expand Down
1 change: 1 addition & 0 deletions config/cc-gcc.make
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ CXX_MINOR = $(shell $(CXX) --version | head -n 1 | sed -e 's/.*[ \t]\([0-9]\)\.\
# -----------------------------------------------------------------------------
# compiler options
DEFINES += -D_GNU_SOURCE
CCFLAGS += -fPIC
CCFLAGS += -pipe
CCFLAGS += -funsigned-char
CCFLAGS += -fno-exceptions
Expand Down
4 changes: 3 additions & 1 deletion config/os-linux.make
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,9 @@ LDFLAGS += `${PYTHON_PATH}/bin/python3-config --ldflags 2>/dev/null`
LDFLAGS += -Wl,-rpath -Wl,${PYTHON_PATH}/lib
else
INCLUDES += `python3-config --includes 2>/dev/null`
LDFLAGS += `python3-config --ldflags 2>/dev/null`
INCLUDES += -I$(shell python3 -c 'import numpy as np; print(np.get_include())')
INCLUDES += `python3 -m pybind11 --includes 2>/dev/null`
LDFLAGS += `python3-config --ldflags --embed 2>/dev/null`
# IF you want to use Python2 for whatever reason:
# INCLUDES += `pkg-config --cflags python`
# LDFLAGS += `pkg-config --libs python`
Expand Down
40 changes: 40 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"""Setup.py inspired by https://thomastrapp.com/posts/building-a-pypi-package-for-a-modern-cpp-project/"""
import os
from setuptools import setup, dist

PACKAGE_NAME = "librasr" # keyword to import
LIBRASR_SUBDIR = os.path.join("src", "Tools", "LibRASR")
LIBRASR_SO = "librasr.so"

assert os.path.exists(os.path.join(LIBRASR_SUBDIR, LIBRASR_SO)), \
"Could not find librasr shared library. Did you compile it correctly?"
class BinaryDistribution(dist.Distribution):
"""Helper class: We assume that librasr.so has already been compiled."""
def has_ext_modules(self) -> bool:
return True

# get README content for long description
this_directory = os.path.abspath(os.path.dirname(__file__))
with open(os.path.join(this_directory, 'README')) as f:
long_description = f.read()

setup(
name='RASR',

# include shared library and link to lower case package name
packages=[PACKAGE_NAME],
package_dir={PACKAGE_NAME: LIBRASR_SUBDIR},
package_data={PACKAGE_NAME: [LIBRASR_SO]},
include_package_data=True,

description="RASR as a python module.",
long_description=long_description,
long_description_content_type="text/markdown",

# use custom distribution class to export librasr.so
distclass=BinaryDistribution,

version='0.0.1',
url='https://github.com/rwth-i6/rasr',
author_email='rwthasr@i6.informatik.rwth-aachen.de',
)
20 changes: 4 additions & 16 deletions src/Core/Configuration.cc
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ void Configuration::Resource::writeUsage(XmlWriter& os) const {
* Central storage place for all resources.
*/

class Configuration::ResourceDataBase {
class Configuration::ResourceDataBase : public ReferenceCounted {
private:
std::set<Resource> resources;
Resource noResource_;
Expand Down Expand Up @@ -401,24 +401,19 @@ bool Configuration::isWellFormedParameterName(const std::string& s) {
}

Configuration::Configuration()
: db_(0),
isDataBaseOwner_(true),
: db_(new ResourceDataBase),
selection_("UNNAMED"),
name_("UNNAMED") {
// define new root configuration
db_ = new ResourceDataBase;
}

Configuration::Configuration(const Configuration& c)
: db_(c.db_),
isDataBaseOwner_(false),
selection_(c.selection_),
name_(c.name_) {
}

Configuration::Configuration(const Configuration& c, const std::string& add_selection)
: db_(c.db_),
isDataBaseOwner_(false) {
: db_(c.db_) {
require(add_selection.length());
require(add_selection.find(resource_separation_char) == std::string::npos);
require(add_selection.find(resource_wildcard_char) == std::string::npos);
Expand All @@ -428,16 +423,9 @@ Configuration::Configuration(const Configuration& c, const std::string& add_sele
ensure(isWellFormedParameterName(selection_));
}

Configuration::~Configuration() {
if (isDataBaseOwner_)
delete db_;
}
Configuration::~Configuration() {}

Configuration& Configuration::operator=(const Configuration& c) {
if (isDataBaseOwner_) {
delete db_;
isDataBaseOwner_ = false;
}
db_ = c.db_;
selection_ = c.selection_;
name_ = c.name_;
Expand Down
8 changes: 4 additions & 4 deletions src/Core/Configuration.hh
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <string.h>
#include <sys/stat.h>
#include "Assertions.hh"
#include "ReferenceCounting.hh"
#include "Types.hh"
#include "XmlStream.hh"

Expand Down Expand Up @@ -112,10 +113,9 @@ public:
class ResourceDataBase;

private:
ResourceDataBase* db_;
bool isDataBaseOwner_;
std::string selection_;
std::string name_;
Ref<ResourceDataBase> db_;
std::string selection_;
std::string name_;

void warning(const std::string& filename, int lineNumber,
const std::string& description);
Expand Down
61 changes: 61 additions & 0 deletions src/Python/AllophoneStateFsaBuilder.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#include "AllophoneStateFsaBuilder.hh"

#include <memory>
#include <vector>

#undef ensure // macro duplication in pybind11/numpy.h
#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>

#include <Core/Application.hh>
#include <Core/Component.hh>
#include <Core/Archive.hh>
#include <Bliss/CorpusDescription.hh>
#include <Nn/AllophoneStateFsaExporter.hh>

namespace py = pybind11;

struct BuildSegmentToOrthMapVisitor : public Bliss::CorpusVisitor {
BuildSegmentToOrthMapVisitor()
: Bliss::CorpusVisitor(), map_(new Core::StringHashMap<std::string>()) {}

virtual void visitSpeechSegment(Bliss::SpeechSegment* s) {
(*map_)[s->fullName()] = s->orth();
}

std::shared_ptr<Core::StringHashMap<std::string>> map_;
};

static std::shared_ptr<Core::StringHashMap<std::string>> build_segment_to_orth_map(Core::Configuration const& config) {
Bliss::CorpusDescription corpus(config);
BuildSegmentToOrthMapVisitor visitor;
corpus.accept(&visitor);
return visitor.map_;
}

AllophoneStateFsaBuilder::AllophoneStateFsaBuilder(const Core::Configuration& c)
: Core::Component(c),
allophoneStateFsaExporter_(nullptr),
segmentToOrthMap_(nullptr) {
// init exporter and orth map
allophoneStateFsaExporter_ = std::make_shared<Nn::AllophoneStateFsaExporter>(select("alignment-fsa-exporter"));
segmentToOrthMap_ = build_segment_to_orth_map(select("corpus"));
}

py::tuple AllophoneStateFsaBuilder::buildBySegmentName(const std::string& segmentName) {
auto iter = segmentToOrthMap_->find(segmentName);
if (iter == segmentToOrthMap_->end()) {
throw std::invalid_argument("Could not find segment with name " + segmentName);
}
return buildByOrthography(iter->second);
}

py::tuple AllophoneStateFsaBuilder::buildByOrthography(const std::string& orthography) {
Nn::AllophoneStateFsaExporter::ExportedAutomaton automaton = allophoneStateFsaExporter_->exportFsaForOrthography(orthography);
return py::make_tuple(
automaton.num_states,
automaton.num_edges,
py::array_t<u32>(automaton.edges.size(), automaton.edges.data()),
py::array_t<f32>(automaton.weights.size(), automaton.weights.data())
);
}
28 changes: 28 additions & 0 deletions src/Python/AllophoneStateFsaBuilder.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#ifndef _PYTHON_ALLOPHONESTATEFSABUILDER_HH
#define _PYTHON_ALLOPHONESTATEFSABUILDER_HH

#include <pybind11/pybind11.h>

#include <Core/Application.hh>
#include <Core/Archive.hh>
#include <Core/Configuration.hh>
#include <Nn/AllophoneStateFsaExporter.hh>

namespace py = pybind11;

class AllophoneStateFsaBuilder : public Core::Component {
public:
AllophoneStateFsaBuilder(const Core::Configuration& c);
~AllophoneStateFsaBuilder() = default;

py::tuple buildBySegmentName(const std::string& segmentName);

py::tuple buildByOrthography(const std::string& orthography);

private:
std::shared_ptr<Nn::AllophoneStateFsaExporter> allophoneStateFsaExporter_;
std::shared_ptr<Core::StringHashMap<std::string>> segmentToOrthMap_;

};

#endif // _PYTHON_ALLOPHONESTATEFSABUILDER_HH
8 changes: 8 additions & 0 deletions src/Python/Configuration.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#include "Configuration.hh"

#include <Core/Configuration.hh>

PyConfiguration::PyConfiguration()
: Core::Configuration() {
setSelection("lib-rasr");
}
11 changes: 11 additions & 0 deletions src/Python/Configuration.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#ifndef _PYTHON_CONFIGURATION_HH
#define _PYTHON_CONFIGURATION_HH

#include <Core/Configuration.hh>

class PyConfiguration : public Core::Configuration {
public:
PyConfiguration();
};

#endif // _PYTHON_CONFIGURATION_HH
11 changes: 9 additions & 2 deletions src/Python/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,21 @@ include $(TOPDIR)/Makefile.cfg

CCFLAGS += -fexceptions

ifdef MODULE_TENSORFLOW
CXXFLAGS += $(TF_CXXFLAGS)
LDFLAGS += $(TF_LDFLAGS)
endif

# -----------------------------------------------------------------------------

SUBDIRS =
TARGETS = libSprintPython.$(a) check$(exe)

LIBPYTHON_O = \
$(OBJDIR)/Init.o \
$(OBJDIR)/Numpy.o \
$(OBJDIR)/AllophoneStateFsaBuilder.o \
$(OBJDIR)/Configuration.o \
$(OBJDIR)/Init.o \
$(OBJDIR)/Numpy.o \
$(OBJDIR)/Utilities.o

CHECK_O = $(OBJDIR)/check.o \
Expand Down
Loading

0 comments on commit f4ed97e

Please sign in to comment.