text: // 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 <vector>
  
  #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, "<DIMENSION>");
    WriteBasicType(out, binary, dim_);
    if (!binary) out << '\n';
    WriteToken(out, binary, "<BETA>");
    WriteBasicType(out, binary, beta_);
    if (!binary) out << '\n';
    WriteToken(out, binary, "<K>");
    Matrix<BaseFloat> tmp_k(K_);
    tmp_k.Write(out, binary);
    WriteToken(out, binary, "<G>");
    int32 g_size = static_cast<int32>(G_.size());
    WriteBasicType(out, binary, g_size);
    if (!binary) out << '\n';
    for (std::vector< SpMatrix<double> >::const_iterator itr = G_.begin(),
        end = G_.end(); itr != end; ++itr) {
      SpMatrix<BaseFloat> tmp_g(*itr);
      tmp_g.Write(out, binary);
    }
  }
  
  void AffineXformStats::Read(std::istream &in, bool binary, bool add) {
    ExpectToken(in, binary, "<DIMENSION>");
    ReadBasicType(in, binary, &dim_);
    ExpectToken(in, binary, "<BETA>");
    ReadBasicType(in, binary, &beta_);
    ExpectToken(in, binary, "<K>");
    Matrix<BaseFloat> tmp_k;
    tmp_k.Read(in, binary);
    K_.Resize(tmp_k.NumRows(), tmp_k.NumCols());
    if (add) {
      Matrix<double> tmp_k_d(tmp_k);
      K_.AddMat(1.0, tmp_k_d, kNoTrans);
    } else {
      K_.CopyFromMat(tmp_k, kNoTrans);
    }
    ExpectToken(in, binary, "<G>");
    int32 g_size;
    ReadBasicType(in, binary, &g_size);
    G_.resize(g_size);
    SpMatrix<BaseFloat> tmp_g;
    SpMatrix<double> 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<double> >::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<BaseFloat> &a, const Matrix<BaseFloat> &b,
                         bool b_is_affine,
                         Matrix<BaseFloat> *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<BaseFloat> b_ext(b.NumRows()+1, b.NumCols());
        SubMatrix<BaseFloat> 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<BaseFloat> b_ext(b.NumRows()+1, b.NumCols()+1);
        SubMatrix<BaseFloat> 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<BaseFloat> &xform,
                            VectorBase<BaseFloat> *vec) {
    int32 dim = xform.NumRows();
    KALDI_ASSERT(dim > 0 && xform.NumCols() == dim+1 && vec->Dim() == dim);
    Vector<BaseFloat> tmp(dim+1);
    SubVector<BaseFloat> 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
  
  