text: // bin/cluster-phones.cc
  
  // Copyright 2009-2011  Microsoft Corporation
  
  // 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 "base/kaldi-common.h"
  #include "util/common-utils.h"
  #include "tree/context-dep.h"
  #include "tree/build-tree.h"
  #include "tree/build-tree-utils.h"
  #include "tree/context-dep.h"
  #include "tree/clusterable-classes.h"
  #include "util/text-utils.h"
  
  
  
  int main(int argc, char *argv[]) {
    using namespace kaldi;
    try {
      using namespace kaldi;
      typedef kaldi::int32 int32;
  
      const char *usage =
          "Cluster phones (or sets of phones) into sets for various purposes\n"
          "Usage:  cluster-phones [options] <tree-stats-in> <phone-sets-in> <clustered-phones-out>\n"
          "e.g.: \n"
          " cluster-phones 1.tacc phonesets.txt questions.txt\n";
      // Format of phonesets.txt is e.g.
      // 1
      // 2 3 4
      // 5 6
      // ...
      // Format of questions.txt output is similar, but with more lines (and the same phone
      // may appear on multiple lines).
  
      // bool binary = true;
      int32 P = 1, N = 3; // Note: N does not matter.
      std::string pdf_class_list_str = "1";  // 1 is just the central position of 3.
      std::string mode = "questions";
      int32 num_classes = -1;
  
      ParseOptions po(usage);
      // po.Register("binary", &binary, "Write output in binary mode");
      po.Register("central-position", &P, "Central position in context window [must match acc-tree-stats]");
      po.Register("context-width", &N, "Does not have any effect-- included for scripting convenience.");
      po.Register("pdf-class-list", &pdf_class_list_str, "Colon-separated list of HMM positions to consider [Default = 1: just central position for 3-state models].");
      po.Register("mode", &mode, "Mode of operation: \"questions\"->sets suitable for decision trees; \"k-means\"->k-means algorithm, output k classes (set num-classes options)\n");
      po.Register("num-classes", &num_classes, "For k-means mode, number of classes.");
  
  
      po.Read(argc, argv);
  
      if (po.NumArgs() != 3) {
        po.PrintUsage();
        exit(1);
      }
  
  
      std::string stats_rxfilename = po.GetArg(1),
          phone_sets_rxfilename = po.GetArg(2),
          phone_sets_wxfilename = po.GetArg(3);
  
  
      BuildTreeStatsType stats;
      {  // Read tree stats.
        bool binary_in;
        GaussClusterable gc;  // dummy needed to provide type.
        Input ki(stats_rxfilename, &binary_in);
        ReadBuildTreeStats(ki.Stream(), binary_in, gc, &stats);
      }
  
      std::vector<int32> pdf_class_list;
      if (!SplitStringToIntegers(pdf_class_list_str, ":", false, &pdf_class_list)
         || pdf_class_list.empty()) {
        KALDI_ERR << "Invalid pdf-class-list string [expecting colon-separated list of integers]: " 
                   << pdf_class_list_str;
      }
  
      std::vector<std::vector< int32> > phone_sets;
      if (!ReadIntegerVectorVectorSimple(phone_sets_rxfilename, &phone_sets))
        KALDI_ERR << "Could not read phone sets from "
                   << PrintableRxfilename(phone_sets_rxfilename);
  
      if (phone_sets.size() == 0)
        KALDI_ERR << "No phone sets in phone sets file ";
  
      std::vector<std::vector<int32> > phone_sets_out;
  
      if (mode == "questions") {
        if (num_classes != -1)
          KALDI_ERR << "num-classes option is not (currently) compatible "
              "with \"questions\" mode.";
        AutomaticallyObtainQuestions(stats,
                                     phone_sets,
                                     pdf_class_list,
                                     P,
                                     &phone_sets_out);
      } else if (mode == "k-means") {
        if (num_classes <= 1 ||
  		 static_cast<size_t>(num_classes) > phone_sets.size())
          KALDI_ERR << "num-classes invalid: num_classes is " << num_classes
                    << ", number of phone sets is " << phone_sets.size();
        KMeansClusterPhones(stats,
                            phone_sets,
                            pdf_class_list,
                            P,
                            num_classes,
                            &phone_sets_out);
      }
  
      if (!WriteIntegerVectorVectorSimple(phone_sets_wxfilename, phone_sets_out))
        KALDI_ERR << "Error writing questions to "
                   << PrintableWxfilename(phone_sets_wxfilename);
      else
        KALDI_LOG << "Wrote questions to "<<phone_sets_wxfilename;
  
      DeleteBuildTreeStats(&stats);
    } catch(const std::exception &e) {
      std::cerr << e.what();
      return -1;
    }
  }
  