// transform/transform-common.cc // Copyright 2009-2011 Saarland University; Microsoft Corporation // See ../../COPYING for clarification regarding multiple authors // // 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 // // THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED // WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, // MERCHANTABLITY OR NON-INFRINGEMENT. // See the Apache 2 License for the specific language governing permissions and // limitations under the License. #include #include "base/kaldi-common.h" #include "transform/transform-common.h" namespace kaldi { void AffineXformStats::Init(int32 dim, int32 num_gs) { if (dim == 0) { if (num_gs != 0) { KALDI_WARN << "Ignoring 'num_gs' (=" << num_gs << ") argument since " << "dim = 0."; } beta_ = 0.0; K_.Resize(0, 0); G_.clear(); dim_ = 0; } else { beta_ = 0.0; K_.Resize(dim, dim + 1, kSetZero); G_.resize(num_gs); for (int32 i = 0; i < num_gs; i++) G_[i].Resize(dim + 1, kSetZero); dim_ = dim; } } void AffineXformStats::Write(std::ostream &out, bool binary) const { WriteToken(out, binary, ""); WriteBasicType(out, binary, dim_); if (!binary) out << '\n'; WriteToken(out, binary, ""); WriteBasicType(out, binary, beta_); if (!binary) out << '\n'; WriteToken(out, binary, ""); Matrix tmp_k(K_); tmp_k.Write(out, binary); WriteToken(out, binary, ""); int32 g_size = static_cast(G_.size()); WriteBasicType(out, binary, g_size); if (!binary) out << '\n'; for (std::vector< SpMatrix >::const_iterator itr = G_.begin(), end = G_.end(); itr != end; ++itr) { SpMatrix tmp_g(*itr); tmp_g.Write(out, binary); } } void AffineXformStats::Read(std::istream &in, bool binary, bool add) { ExpectToken(in, binary, ""); ReadBasicType(in, binary, &dim_); ExpectToken(in, binary, ""); ReadBasicType(in, binary, &beta_); ExpectToken(in, binary, ""); Matrix tmp_k; tmp_k.Read(in, binary); K_.Resize(tmp_k.NumRows(), tmp_k.NumCols()); if (add) { Matrix tmp_k_d(tmp_k); K_.AddMat(1.0, tmp_k_d, kNoTrans); } else { K_.CopyFromMat(tmp_k, kNoTrans); } ExpectToken(in, binary, ""); int32 g_size; ReadBasicType(in, binary, &g_size); G_.resize(g_size); SpMatrix tmp_g; SpMatrix tmp_g_d; if (add) { tmp_g_d.Resize(tmp_g.NumRows()); } for (size_t i = 0; i < G_.size(); i++) { tmp_g.Read(in, binary, false /*no add*/); G_[i].Resize(tmp_g.NumRows()); if (add) { tmp_g_d.CopyFromSp(tmp_g); G_[i].AddSp(1.0, tmp_g_d); } else { G_[i].CopyFromSp(tmp_g); } } } void AffineXformStats::SetZero() { beta_ = 0.0; K_.SetZero(); for (std::vector< SpMatrix >::iterator it = G_.begin(), end = G_.end(); it != end; ++it) { it->SetZero(); } } void AffineXformStats::CopyStats(const AffineXformStats &other) { KALDI_ASSERT(G_.size() == other.G_.size()); KALDI_ASSERT(dim_ == other.dim_); beta_ = other.beta_; K_.CopyFromMat(other.K_, kNoTrans); for (size_t i = 0; i < G_.size(); i++) G_[i].CopyFromSp(other.G_[i]); } void AffineXformStats::Add(const AffineXformStats &other) { KALDI_ASSERT(G_.size() == other.G_.size()); KALDI_ASSERT(dim_ == other.dim_); beta_ += other.beta_; K_.AddMat(1.0, other.K_, kNoTrans); for (size_t i = 0; i < G_.size(); i++) G_[i].AddSp(1.0, other.G_[i]); } bool ComposeTransforms(const Matrix &a, const Matrix &b, bool b_is_affine, Matrix *c) { if (b.NumRows() == 0 || a.NumCols() == 0) { KALDI_WARN << "Empty matrix in ComposeTransforms"; return false; } if (a.NumCols() == b.NumRows()) { c->Resize(a.NumRows(), b.NumCols()); c->AddMatMat(1.0, a, kNoTrans, b, kNoTrans, 0.0); // c = a * b. return true; } else if (a.NumCols() == b.NumRows()+1) { // a is affine. if (b_is_affine) { // append 0 0 0 0 ... 1 to b and multiply. Matrix b_ext(b.NumRows()+1, b.NumCols()); SubMatrix b_part(b_ext, 0, b.NumRows(), 0, b.NumCols()); b_part.CopyFromMat(b); b_ext(b.NumRows(), b.NumCols()-1) = 1.0; // so the last row is 0 0 0 0 ... 0 1 c->Resize(a.NumRows(), b.NumCols()); c->AddMatMat(1.0, a, kNoTrans, b_ext, kNoTrans, 0.0); // c = a * b_ext. } else { // extend b by 1 row and column with all zeros except a 1 on diagonal. Matrix b_ext(b.NumRows()+1, b.NumCols()+1); SubMatrix b_part(b_ext, 0, b.NumRows(), 0, b.NumCols()); b_part.CopyFromMat(b); b_ext(b.NumRows(), b.NumCols()) = 1.0; // so the last row is 0 0 0 0 ... 0 1; // rest of last column is zero (this is the offset term) c->Resize(a.NumRows(), b.NumCols()+1); c->AddMatMat(1.0, a, kNoTrans, b_ext, kNoTrans, 0.0); // c = a * b_ext. } return true; } else { KALDI_ERR << "ComposeTransforms: mismatched dimensions, a has " << a.NumCols() << " columns and b has " << b.NumRows() << " rows."; // this is fatal. return false; } } void ApplyAffineTransform(const MatrixBase &xform, VectorBase *vec) { int32 dim = xform.NumRows(); KALDI_ASSERT(dim > 0 && xform.NumCols() == dim+1 && vec->Dim() == dim); Vector tmp(dim+1); SubVector tmp_part(tmp, 0, dim); tmp_part.CopyFromVec(*vec); tmp(dim) = 1.0; // next line is: vec = 1.0 * xform * tmp + 0.0 * vec vec->AddMatVec(1.0, xform, kNoTrans, tmp, 0.0); } } // namespace kaldi