<commit_before>#include <iostream>
#include <tr1/memory>
#include <queue>

#include <boost/functional.hpp>
#include <boost/program_options.hpp>
#include <boost/program_options/variables_map.hpp>

#include "corpus_tools.h"
#include "m.h"
#include "tdict.h"
#include "sampler.h"
#include "ccrp.h"
#include "ccrp_onetable.h"

using namespace std;
using namespace tr1;
namespace po = boost::program_options;

shared_ptr<MT19937> prng;

void InitCommandLine(int argc, char** argv, po::variables_map* conf) {
  po::options_description opts("Configuration options");
  opts.add_options()
        ("samples,s",po::value<unsigned>()->default_value(1000),"Number of samples")
        ("input,i",po::value<string>(),"Read data from")
        ("random_seed,S",po::value<uint32_t>(), "Random seed");
  po::options_description clo("Command line options");
  clo.add_options()
        ("config", po::value<string>(), "Configuration file")
        ("help", "Print this help message and exit");
  po::options_description dconfig_options, dcmdline_options;
  dconfig_options.add(opts);
  dcmdline_options.add(opts).add(clo);

  po::store(parse_command_line(argc, argv, dcmdline_options), *conf);
  if (conf->count("config")) {
    ifstream config((*conf)["config"].as<string>().c_str());
    po::store(po::parse_config_file(config, dconfig_options), *conf);
  }
  po::notify(*conf);

  if (conf->count("help") || (conf->count("input") == 0)) {
    cerr << dcmdline_options << endl;
    exit(1);
  }
}

template <unsigned N> struct PYPLM;

// uniform base distribution
template<> struct PYPLM<0> {
  PYPLM(unsigned vs) : p0(1.0 / vs), draws() {}
  void increment(WordID w, const vector<WordID>& context, MT19937* rng) { ++draws; }
  void decrement(WordID w, const vector<WordID>& context, MT19937* rng) { --draws; assert(draws >= 0); }
  double prob(WordID w, const vector<WordID>& context) const { return p0; }
  void resample_hyperparameters(MT19937* rng, const unsigned nloop, const unsigned niterations) {}
  double log_likelihood() const { return draws * log(p0); }
  const double p0;
  int draws;
};

// represents an N-gram LM
template <unsigned N> struct PYPLM {
  PYPLM(unsigned vs) : backoff(vs), d(0.8), alpha(1.0) {}
  void increment(WordID w, const vector<WordID>& context, MT19937* rng) {
    const double bo = backoff.prob(w, context);
    static vector<WordID> lookup(N-1);
    for (unsigned i = 0; i < N-1; ++i)
      lookup[i] = context[context.size() - 1 - i];
    typename unordered_map<vector<WordID>, CCRP<WordID>, boost::hash<vector<WordID> > >::iterator it = p.find(lookup);
    if (it == p.end())
      it = p.insert(make_pair(lookup, CCRP<WordID>(d,alpha))).first;
    if (it->second.increment(w, bo, rng))
      backoff.increment(w, context, rng);
  }
  void decrement(WordID w, const vector<WordID>& context, MT19937* rng) {
    static vector<WordID> lookup(N-1);
    for (unsigned i = 0; i < N-1; ++i)
      lookup[i] = context[context.size() - 1 - i];
    typename unordered_map<vector<WordID>, CCRP<WordID>, boost::hash<vector<WordID> > >::iterator it = p.find(lookup);
    assert(it != p.end());
    if (it->second.decrement(w, rng))
      backoff.decrement(w, context, rng);
  }
  double prob(WordID w, const vector<WordID>& context) const {
    const double bo = backoff.prob(w, context);
    static vector<WordID> lookup(N-1);
    for (unsigned i = 0; i < N-1; ++i)
      lookup[i] = context[context.size() - 1 - i];
    typename unordered_map<vector<WordID>, CCRP<WordID>, boost::hash<vector<WordID> > >::const_iterator it = p.find(lookup);
    if (it == p.end()) return bo;
    return it->second.prob(w, bo);
  }

  double log_likelihood() const {
    return log_likelihood(d, alpha) + backoff.log_likelihood();
  }

  double log_likelihood(const double& dd, const double& aa) const {
    if (aa <= -dd) return -std::numeric_limits<double>::infinity();
    double llh = Md::log_beta_density(dd, 1, 1) + Md::log_gamma_density(aa, 1, 1);
    typename unordered_map<vector<WordID>, CCRP<WordID>, boost::hash<vector<WordID> > >::const_iterator it;
    for (it = p.begin(); it != p.end(); ++it)
      llh += it->second.log_crp_prob(dd, aa);
    return llh;
  }

  struct DiscountResampler {
    DiscountResampler(const PYPLM& m) : m_(m) {}
    const PYPLM& m_;
    double operator()(const double& proposed_discount) const {
      return m_.log_likelihood(proposed_discount, m_.alpha);
    }
  };

  struct AlphaResampler {
    AlphaResampler(const PYPLM& m) : m_(m) {}
    const PYPLM& m_;
    double operator()(const double& proposed_alpha) const {
      return m_.log_likelihood(m_.d, proposed_alpha);
    }
  };

  void resample_hyperparameters(MT19937* rng, const unsigned nloop = 5, const unsigned niterations = 10) {
    DiscountResampler dr(*this);
    AlphaResampler ar(*this);
    for (int iter = 0; iter < nloop; ++iter) {
      alpha = slice_sampler1d(ar, alpha, *rng, 0.0,
                              std::numeric_limits<double>::infinity(), 0.0, niterations, 100*niterations);
      d = slice_sampler1d(dr, d, *rng, std::numeric_limits<double>::min(),
                          1.0, 0.0, niterations, 100*niterations);
    }
    alpha = slice_sampler1d(ar, alpha, *rng, 0.0,
                            std::numeric_limits<double>::infinity(), 0.0, niterations, 100*niterations);
    typename unordered_map<vector<WordID>, CCRP<WordID>, boost::hash<vector<WordID> > >::iterator it;
    cerr << "PYPLM<" << N << ">(d=" << d << ",a=" << alpha << ") = " << log_likelihood(d, alpha) << endl;
    for (it = p.begin(); it != p.end(); ++it) {
      it->second.set_discount(d);
      it->second.set_alpha(alpha);
    }
    backoff.resample_hyperparameters(rng, nloop, niterations);
  }

  PYPLM<N-1> backoff;
  double d, alpha;
  unordered_map<vector<WordID>, CCRP<WordID>, boost::hash<vector<WordID> > > p;
};

int main(int argc, char** argv) {
  po::variables_map conf;

  InitCommandLine(argc, argv, &conf);
  const unsigned samples = conf["samples"].as<unsigned>();
  if (conf.count("random_seed"))
    prng.reset(new MT19937(conf["random_seed"].as<uint32_t>()));
  else
    prng.reset(new MT19937);
  MT19937& rng = *prng;
  vector<vector<WordID> > corpuse;
  set<WordID> vocabe;
  const WordID kEOS = TD::Convert("</s>");
  cerr << "Reading corpus...\n";
  CorpusTools::ReadFromFile(conf["input"].as<string>(), &corpuse, &vocabe);
  cerr << "E-corpus size: " << corpuse.size() << " sentences\t (" << vocabe.size() << " word types)\n";
#define kORDER 3
  PYPLM<kORDER> lm(vocabe.size());
  vector<WordID> ctx(kORDER - 1, TD::Convert("<s>"));
  int mci = corpuse.size() * 99 / 100;
  for (int SS=0; SS < samples; ++SS) {
    for (int ci = 0; ci < mci; ++ci) {
      ctx.resize(kORDER - 1);
      const vector<WordID>& s = corpuse[ci];
      for (int i = 0; i <= s.size(); ++i) {
        WordID w = (i < s.size() ? s[i] : kEOS);
        if (SS > 0) lm.decrement(w, ctx, &rng);
        lm.increment(w, ctx, &rng);
        ctx.push_back(w);
      }
      if (SS > 0) lm.decrement(kEOS, ctx, &rng);
      lm.increment(kEOS, ctx, &rng);
    }
    if (SS % 10 == 9) {
      cerr << " [LLH=" << lm.log_likelihood() << "]" << endl;
      if (SS % 20 == 19) lm.resample_hyperparameters(&rng);
    } else { cerr << '.' << flush; }
  }
  double llh = 0;
  unsigned cnt = 0;
  for (int ci = mci; ci < corpuse.size(); ++ci) {
    ctx.resize(kORDER - 1);
    const vector<WordID>& s = corpuse[ci];
    for (int i = 0; i <= s.size(); ++i) {
      WordID w = (i < s.size() ? s[i] : kEOS);
      double lp = log(lm.prob(w, ctx)) / log(2);
      cerr << "p(" << TD::Convert(w) << " | " << TD::GetString(ctx) << ") = " << lp << endl;
      ctx.push_back(w);
      llh -= lp;
      cnt++;
    }
  }
  cerr << "  Log_10 prob: " << (llh * log(2) / log(10)) << endl;
  cerr << "        Count: " << (cnt) << endl;
  cerr << "Cross-entropy: " << (llh / cnt) << endl;
  cerr << "   Perplexity: " << pow(2, llh / cnt) << endl;
  return 0;
}

<commit_msg>clean up pyp lm code<commit_after>#include <iostream>
#include <tr1/memory>
#include <queue>

#include <boost/functional.hpp>
#include <boost/program_options.hpp>
#include <boost/program_options/variables_map.hpp>

#include "corpus_tools.h"
#include "m.h"
#include "tdict.h"
#include "sampler.h"
#include "ccrp.h"

// A not very memory-efficient implementation of an N-gram LM based on PYPs
// as described in Y.-W. Teh. (2006) A Hierarchical Bayesian Language Model
// based on Pitman-Yor Processes. In Proc. ACL.

// I use templates to handle the recursive formalation of the prior, so
// the order of the model has to be specified here, at compile time:
#define kORDER 3

using namespace std;
using namespace tr1;
namespace po = boost::program_options;

shared_ptr<MT19937> prng;

void InitCommandLine(int argc, char** argv, po::variables_map* conf) {
  po::options_description opts("Configuration options");
  opts.add_options()
        ("samples,s",po::value<unsigned>()->default_value(300),"Number of samples")
        ("train,i",po::value<string>(),"Training data file")
        ("test,T",po::value<string>(),"Test data file")
        ("discount_prior_a,a",po::value<double>()->default_value(1.0), "discount ~ Beta(a,b): a=this")
        ("discount_prior_b,b",po::value<double>()->default_value(1.0), "discount ~ Beta(a,b): b=this")
        ("strength_prior_s,s",po::value<double>()->default_value(1.0), "strength ~ Gamma(s,r): s=this")
        ("strength_prior_r,r",po::value<double>()->default_value(1.0), "strength ~ Gamma(s,r): r=this")
        ("random_seed,S",po::value<uint32_t>(), "Random seed");
  po::options_description clo("Command line options");
  clo.add_options()
        ("config", po::value<string>(), "Configuration file")
        ("help", "Print this help message and exit");
  po::options_description dconfig_options, dcmdline_options;
  dconfig_options.add(opts);
  dcmdline_options.add(opts).add(clo);

  po::store(parse_command_line(argc, argv, dcmdline_options), *conf);
  if (conf->count("config")) {
    ifstream config((*conf)["config"].as<string>().c_str());
    po::store(po::parse_config_file(config, dconfig_options), *conf);
  }
  po::notify(*conf);

  if (conf->count("help") || (conf->count("train") == 0)) {
    cerr << dcmdline_options << endl;
    exit(1);
  }
}

template <unsigned N> struct PYPLM;

// uniform base distribution (0-gram model)
template<> struct PYPLM<0> {
  PYPLM(unsigned vs, double, double, double, double) : p0(1.0 / vs), draws() {}
  void increment(WordID, const vector<WordID>&, MT19937*) { ++draws; }
  void decrement(WordID, const vector<WordID>&, MT19937*) { --draws; assert(draws >= 0); }
  double prob(WordID, const vector<WordID>&) const { return p0; }
  void resample_hyperparameters(MT19937*, const unsigned, const unsigned) {}
  double log_likelihood() const { return draws * log(p0); }
  const double p0;
  int draws;
};

// represents an N-gram LM
template <unsigned N> struct PYPLM {
  PYPLM(unsigned vs, double da, double db, double ss, double sr) :
      backoff(vs, da, db, ss, sr),
      discount_a(da), discount_b(db),
      strength_s(ss), strength_r(sr),
      d(0.8), alpha(1.0), lookup(N-1) {}
  void increment(WordID w, const vector<WordID>& context, MT19937* rng) {
    const double bo = backoff.prob(w, context);
    for (unsigned i = 0; i < N-1; ++i)
      lookup[i] = context[context.size() - 1 - i];
    typename unordered_map<vector<WordID>, CCRP<WordID>, boost::hash<vector<WordID> > >::iterator it = p.find(lookup);
    if (it == p.end())
      it = p.insert(make_pair(lookup, CCRP<WordID>(d,alpha))).first;
    if (it->second.increment(w, bo, rng))
      backoff.increment(w, context, rng);
  }
  void decrement(WordID w, const vector<WordID>& context, MT19937* rng) {
    for (unsigned i = 0; i < N-1; ++i)
      lookup[i] = context[context.size() - 1 - i];
    typename unordered_map<vector<WordID>, CCRP<WordID>, boost::hash<vector<WordID> > >::iterator it = p.find(lookup);
    assert(it != p.end());
    if (it->second.decrement(w, rng))
      backoff.decrement(w, context, rng);
  }
  double prob(WordID w, const vector<WordID>& context) const {
    const double bo = backoff.prob(w, context);
    for (unsigned i = 0; i < N-1; ++i)
      lookup[i] = context[context.size() - 1 - i];
    typename unordered_map<vector<WordID>, CCRP<WordID>, boost::hash<vector<WordID> > >::const_iterator it = p.find(lookup);
    if (it == p.end()) return bo;
    return it->second.prob(w, bo);
  }

  double log_likelihood() const {
    return log_likelihood(d, alpha) + backoff.log_likelihood();
  }

  double log_likelihood(const double& dd, const double& aa) const {
    if (aa <= -dd) return -std::numeric_limits<double>::infinity();
    //double llh = Md::log_beta_density(dd, 10, 3) + Md::log_gamma_density(aa, 1, 1);
    double llh = Md::log_beta_density(dd, discount_a, discount_b) +
                 Md::log_gamma_density(aa, strength_s, strength_r);
    typename unordered_map<vector<WordID>, CCRP<WordID>, boost::hash<vector<WordID> > >::const_iterator it;
    for (it = p.begin(); it != p.end(); ++it)
      llh += it->second.log_crp_prob(dd, aa);
    return llh;
  }

  struct DiscountResampler {
    DiscountResampler(const PYPLM& m) : m_(m) {}
    const PYPLM& m_;
    double operator()(const double& proposed_discount) const {
      return m_.log_likelihood(proposed_discount, m_.alpha);
    }
  };

  struct AlphaResampler {
    AlphaResampler(const PYPLM& m) : m_(m) {}
    const PYPLM& m_;
    double operator()(const double& proposed_alpha) const {
      return m_.log_likelihood(m_.d, proposed_alpha);
    }
  };

  void resample_hyperparameters(MT19937* rng, const unsigned nloop = 5, const unsigned niterations = 10) {
    DiscountResampler dr(*this);
    AlphaResampler ar(*this);
    for (int iter = 0; iter < nloop; ++iter) {
      alpha = slice_sampler1d(ar, alpha, *rng, 0.0,
                              std::numeric_limits<double>::infinity(), 0.0, niterations, 100*niterations);
      d = slice_sampler1d(dr, d, *rng, std::numeric_limits<double>::min(),
                          1.0, 0.0, niterations, 100*niterations);
    }
    alpha = slice_sampler1d(ar, alpha, *rng, 0.0,
                            std::numeric_limits<double>::infinity(), 0.0, niterations, 100*niterations);
    typename unordered_map<vector<WordID>, CCRP<WordID>, boost::hash<vector<WordID> > >::iterator it;
    cerr << "PYPLM<" << N << ">(d=" << d << ",a=" << alpha << ") = " << log_likelihood(d, alpha) << endl;
    for (it = p.begin(); it != p.end(); ++it) {
      it->second.set_discount(d);
      it->second.set_alpha(alpha);
    }
    backoff.resample_hyperparameters(rng, nloop, niterations);
  }

  PYPLM<N-1> backoff;
  double discount_a, discount_b, strength_s, strength_r;
  double d, alpha;
  mutable vector<WordID> lookup;  // thread-local
  unordered_map<vector<WordID>, CCRP<WordID>, boost::hash<vector<WordID> > > p;
};

int main(int argc, char** argv) {
  po::variables_map conf;

  InitCommandLine(argc, argv, &conf);
  const unsigned samples = conf["samples"].as<unsigned>();
  if (conf.count("random_seed"))
    prng.reset(new MT19937(conf["random_seed"].as<uint32_t>()));
  else
    prng.reset(new MT19937);
  MT19937& rng = *prng;
  vector<vector<WordID> > corpuse;
  set<WordID> vocabe;
  const WordID kEOS = TD::Convert("</s>");
  cerr << "Reading corpus...\n";
  CorpusTools::ReadFromFile(conf["train"].as<string>(), &corpuse, &vocabe);
  cerr << "E-corpus size: " << corpuse.size() << " sentences\t (" << vocabe.size() << " word types)\n";
  vector<vector<WordID> > test;
  if (conf.count("test"))
    CorpusTools::ReadFromFile(conf["test"].as<string>(), &test);
  else
    test = corpuse;
  PYPLM<kORDER> lm(vocabe.size(),
                   conf["discount_prior_a"].as<double>(),
                   conf["discount_prior_b"].as<double>(),
                   conf["strength_prior_s"].as<double>(),
                   conf["strength_prior_r"].as<double>());
  vector<WordID> ctx(kORDER - 1, TD::Convert("<s>"));
  for (int SS=0; SS < samples; ++SS) {
    for (int ci = 0; ci < corpuse.size(); ++ci) {
      ctx.resize(kORDER - 1);
      const vector<WordID>& s = corpuse[ci];
      for (int i = 0; i <= s.size(); ++i) {
        WordID w = (i < s.size() ? s[i] : kEOS);
        if (SS > 0) lm.decrement(w, ctx, &rng);
        lm.increment(w, ctx, &rng);
        ctx.push_back(w);
      }
      if (SS > 0) lm.decrement(kEOS, ctx, &rng);
      lm.increment(kEOS, ctx, &rng);
    }
    if (SS % 10 == 9) {
      cerr << " [LLH=" << lm.log_likelihood() << "]" << endl;
      if (SS % 20 == 19) lm.resample_hyperparameters(&rng);
    } else { cerr << '.' << flush; }
  }
  double llh = 0;
  unsigned cnt = 0;
  unsigned oovs = 0;
  for (int ci = 0; ci < test.size(); ++ci) {
    ctx.resize(kORDER - 1);
    const vector<WordID>& s = test[ci];
    for (int i = 0; i <= s.size(); ++i) {
      WordID w = (i < s.size() ? s[i] : kEOS);
      double lp = log(lm.prob(w, ctx)) / log(2);
      if (i < s.size() && vocabe.count(w) == 0) {
        cerr << "**OOV ";
        ++oovs;
        lp = 0;
      }
      cerr << "p(" << TD::Convert(w) << " |";
      for (int j = ctx.size() + 1 - kORDER; j < ctx.size(); ++j)
        cerr << ' ' << TD::Convert(ctx[j]);
      cerr << ") = " << lp << endl;
      ctx.push_back(w);
      llh -= lp;
      cnt++;
    }
  }
  cerr << "  Log_10 prob: " << (-llh * log(2) / log(10)) << endl;
  cerr << "        Count: " << cnt << endl;
  cerr << "         OOVs: " << oovs << endl;
  cerr << "Cross-entropy: " << (llh / cnt) << endl;
  cerr << "   Perplexity: " << pow(2, llh / cnt) << endl;
  return 0;
}

<|endoftext|>


<commit_before>/*
 *  nextpnr -- Next Generation Place and Route
 *
 *  Copyright (C) 2018  David Shah <david@symbioticeda.com>
 *
 *  Permission to use, copy, modify, and/or distribute this software for any
 *  purpose with or without fee is hereby granted, provided that the above
 *  copyright notice and this permission notice appear in all copies.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */

#include "place_legaliser.h"
#include <algorithm>
#include <vector>
#include "cells.h"
#include "design_utils.h"
#include "log.h"
#include "util.h"

NEXTPNR_NAMESPACE_BEGIN

struct CellChain
{
    std::vector<CellInfo *> cells;
};

// Generic chain finder
template <typename F1, typename F2, typename F3>
std::vector<CellChain> find_chains(const Context *ctx, F1 cell_type_predicate, F2 get_previous, F3 get_next,
                                   size_t min_length = 2)
{
    std::set<IdString> chained;
    std::vector<CellChain> chains;
    for (auto cell : sorted(ctx->cells)) {
        if (chained.find(cell.first) != chained.end())
            continue;
        CellInfo *ci = cell.second;
        if (cell_type_predicate(ctx, ci)) {
            CellInfo *start = ci;
            CellInfo *prev_start = ci;
            while (prev_start != nullptr) {
                start = prev_start;
                prev_start = get_previous(ctx, start);
            }
            CellChain chain;
            CellInfo *end = start;
            while (end != nullptr) {
                chain.cells.push_back(end);
                end = get_next(ctx, end);
            }
            if (chain.cells.size() >= min_length) {
                chains.push_back(chain);
                for (auto c : chain.cells)
                    chained.insert(c->name);
            }
        }
    }
    return chains;
}

static void get_chain_midpoint(const Context *ctx, const std::vector<CellInfo *> &chain, float &x, float &y)
{
    float total_x = 0, total_y = 0;
    int N = 0;
    for (auto cell : chain) {
        if (cell->bel == BelId())
            continue;
        int bel_x, bel_y;
        bool bel_gb;
        ctx->estimatePosition(cell->bel, bel_x, bel_y, bel_gb);
        total_x += bel_x;
        total_y += bel_y;
        N++;
    }
    assert(N > 0);
    x = total_x / N;
    y = total_y / N;
}

class PlacementLegaliser
{
  public:
    PlacementLegaliser(Context *ctx) : ctx(ctx){};
    bool legalise()
    {
        bool legalised_carries = legalise_carries();
        if (!legalised_carries && !ctx->force)
            return false;
        return legalised_carries;
    }

  private:
    bool legalise_carries()
    {
        std::vector<CellChain> carry_chains = find_chains(
                ctx, is_lc,
                [](const Context *ctx, const CellInfo *cell) {
                    return net_driven_by(ctx, cell->ports.at(ctx->id("CIN")).net, is_lc, ctx->id("COUT"));
                },
                [](const Context *ctx, const CellInfo *cell) {
                    return net_only_drives(ctx, cell->ports.at(ctx->id("COUT")).net, is_lc, ctx->id("CIN"), false);
                });
        for (auto chain : carry_chains) {
        }
        return true;
    }

    // Split a carry chain into multiple legal chains
    std::vector<CellChain> split_carry_chain(Context *ctx, CellChain &carryc)
    {
        bool start_of_chain = true;
        std::vector<CellChain> chains;
        std::vector<const CellInfo *> tile;
        const int max_length = (ctx->chip_info->height - 2) * 8 - 2;
        auto curr_cell = carryc.cells.begin();
        while (curr_cell != carryc.cells.end()) {
            CellInfo *cell = *curr_cell;
            if (tile.size() >= 8) {
                tile.clear();
            }
            if (start_of_chain) {
                tile.clear();
                chains.emplace_back();
                start_of_chain = false;
                if (cell->ports.at(ctx->id("CIN")).net) {
                    // CIN is not constant and not part of a chain. Must feed in from fabric
                    CellInfo *feedin = make_carry_feed_in(cell, cell->ports.at(ctx->id("CIN")));
                    chains.back().cells.push_back(feedin);
                    tile.push_back(feedin);
                }
            }
            tile.push_back(cell);
            chains.back().cells.push_back(cell);
            bool split_chain = (!ctx->logicCellsCompatible(tile)) || (int(chains.back().cells.size()) > max_length);
            if (split_chain) {
                CellInfo *passout = make_carry_pass_out(cell->ports.at(ctx->id("COUT")));
                tile.pop_back();
                chains.back().cells.back() = passout;
                start_of_chain = true;
            } else {
                NetInfo *carry_net = cell->ports.at(ctx->id("COUT")).net;
                if (carry_net != nullptr && carry_net->users.size() > 1) {
                    CellInfo *passout = make_carry_pass_out(cell->ports.at(ctx->id("COUT")));
                    chains.back().cells.push_back(passout);
                    tile.push_back(passout);
                }
                ++curr_cell;
            }
        }
        return chains;
    }

    // Insert a logic cell to legalise a COUT->fabric connection
    CellInfo *make_carry_pass_out(PortInfo &cout_port)
    {
        assert(cout_port.net != nullptr);
        CellInfo *lc = create_ice_cell(ctx, ctx->id("ICESTORM_LC"));
        lc->params[ctx->id("LUT_INIT")] = "65280"; // 0xff00: O = I3
        lc->params[ctx->id("CARRY_ENABLE")] = "1";
        lc->ports.at(ctx->id("O")).net = cout_port.net;
        NetInfo *co_i3_net = new NetInfo();
        co_i3_net->name = ctx->id(lc->name.str(ctx) + "$I3");
        co_i3_net->driver = cout_port.net->driver;
        PortRef i3_r;
        i3_r.port = ctx->id("I3");
        i3_r.cell = lc;
        co_i3_net->users.push_back(i3_r);
        PortRef o_r;
        o_r.port = ctx->id("O");
        o_r.cell = lc;
        cout_port.net->driver = o_r;
        lc->ports.at(ctx->id("I3")).net = co_i3_net;
        // I1=1 feeds carry up the chain, so no need to actually break the chain
        lc->ports.at(ctx->id("I1")).net = ctx->nets.at(ctx->id("$PACKER_VCC_NET"));
        PortRef i1_r;
        i1_r.port = ctx->id("I1");
        i1_r.cell = lc;
        ctx->nets.at(ctx->id("$PACKER_VCC_NET"))->users.push_back(i1_r);
        ctx->cells[lc->name] = lc;
        createdCells.insert(lc->name);
        return lc;
    }

    // Insert a logic cell to legalise a CIN->fabric connection
    CellInfo *make_carry_feed_in(CellInfo *cin_cell, PortInfo &cin_port)
    {
        assert(cin_port.net != nullptr);
        CellInfo *lc = create_ice_cell(ctx, ctx->id("ICESTORM_LC"));
        lc->params[ctx->id("CARRY_ENABLE")] = "1";
        lc->params[ctx->id("CIN_CONST")] = "1";
        lc->params[ctx->id("CIN_SET")] = "1";
        lc->ports.at(ctx->id("I1")).net = cin_port.net;
        cin_port.net->users.erase(std::remove_if(cin_port.net->users.begin(), cin_port.net->users.end(),
                                                 [cin_cell, cin_port](const PortRef &usr) {
                                                     return usr.cell == cin_cell && usr.port == cin_port.name;
                                                 }));
        NetInfo *out_net = new NetInfo();
        out_net->name = ctx->id(lc->name.str(ctx) + "$O");

        ctx->cells[lc->name] = lc;
        createdCells.insert(lc->name);
        return lc;
    }

    Context *ctx;
    std::unordered_set<IdString> rippedCells;
    std::unordered_set<IdString> createdCells;
};

bool legalise_design(Context *ctx)
{
    PlacementLegaliser lg(ctx);
    return lg.legalise();
}

NEXTPNR_NAMESPACE_END
<commit_msg>ice40: Working on the placement legaliser<commit_after>/*
 *  nextpnr -- Next Generation Place and Route
 *
 *  Copyright (C) 2018  David Shah <david@symbioticeda.com>
 *
 *  Permission to use, copy, modify, and/or distribute this software for any
 *  purpose with or without fee is hereby granted, provided that the above
 *  copyright notice and this permission notice appear in all copies.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */

#include "place_legaliser.h"
#include <algorithm>
#include <vector>
#include "cells.h"
#include "design_utils.h"
#include "log.h"
#include "util.h"

NEXTPNR_NAMESPACE_BEGIN

struct CellChain
{
    std::vector<CellInfo *> cells;
};

// Generic chain finder
template <typename F1, typename F2, typename F3>
std::vector<CellChain> find_chains(const Context *ctx, F1 cell_type_predicate, F2 get_previous, F3 get_next,
                                   size_t min_length = 2)
{
    std::set<IdString> chained;
    std::vector<CellChain> chains;
    for (auto cell : sorted(ctx->cells)) {
        if (chained.find(cell.first) != chained.end())
            continue;
        CellInfo *ci = cell.second;
        if (cell_type_predicate(ctx, ci)) {
            CellInfo *start = ci;
            CellInfo *prev_start = ci;
            while (prev_start != nullptr) {
                start = prev_start;
                prev_start = get_previous(ctx, start);
            }
            CellChain chain;
            CellInfo *end = start;
            while (end != nullptr) {
                chain.cells.push_back(end);
                end = get_next(ctx, end);
            }
            if (chain.cells.size() >= min_length) {
                chains.push_back(chain);
                for (auto c : chain.cells)
                    chained.insert(c->name);
            }
        }
    }
    return chains;
}

static void get_chain_midpoint(const Context *ctx, const CellChain &chain, float &x, float &y)
{
    float total_x = 0, total_y = 0;
    int N = 0;
    for (auto cell : chain.cells) {
        if (cell->bel == BelId())
            continue;
        int bel_x, bel_y;
        bool bel_gb;
        ctx->estimatePosition(cell->bel, bel_x, bel_y, bel_gb);
        total_x += bel_x;
        total_y += bel_y;
        N++;
    }
    assert(N > 0);
    x = total_x / N;
    y = total_y / N;
}

class PlacementLegaliser
{
  public:
    PlacementLegaliser(Context *ctx) : ctx(ctx){};

    bool legalise()
    {
        bool legalised_carries = legalise_carries();
        if (!legalised_carries && !ctx->force)
            return false;
        return legalised_carries;
    }

  private:
    void init_logic_cells()
    {
        for (auto bel : ctx->getBels()) {
            // Initialise the logic bels vector with unavailable invalid bels, dimensions [0..width][0..height[0..7]
            logic_bels.resize(ctx->chip_info->width + 1,
                              std::vector<std::vector<std::pair<BelId, bool>>>(
                                      ctx->chip_info->height + 1,
                                      std::vector<std::pair<BelId, bool>>(8, std::make_pair(BelId(), true))));
            if (ctx->getBelType(bel) == TYPE_ICESTORM_LC) {
                // Using the non-standard API here to get (x, y, z) rather than just (x, y)
                auto bi = ctx->chip_info->bel_data[bel.index];
                int x = bi.x, y = bi.y, z = bi.z;
                IdString cell = ctx->getBoundBelCell(bel);
                if (cell != IdString() && ctx->cells.at(cell)->belStrength >= STRENGTH_FIXED)
                    logic_bels.at(x).at(y).at(z) = std::make_pair(bel, true);
                else
                    logic_bels.at(x).at(y).at(z) = std::make_pair(bel, false);
            }
        }
    }

    bool legalise_carries()
    {
        std::vector<CellChain> carry_chains = find_chains(
                ctx, is_lc,
                [](const Context *ctx, const CellInfo *cell) {
                    return net_driven_by(ctx, cell->ports.at(ctx->id("CIN")).net, is_lc, ctx->id("COUT"));
                },
                [](const Context *ctx, const CellInfo *cell) {
                    return net_only_drives(ctx, cell->ports.at(ctx->id("COUT")).net, is_lc, ctx->id("CIN"), false);
                });
        int width = ctx->chip_info->width, height = ctx->chip_info->height;
        for (auto &base_chain : carry_chains) {
            std::vector<CellChain> split_chains = split_carry_chain(base_chain);
            for (auto &chain : split_chains) {
                float mid_x, mid_y;
                get_chain_midpoint(ctx, chain, mid_x, mid_y);
                float base_x = mid_x, base_y = mid_y - (chain.cells.size() / 16.0f);
                // Find Bel meeting requirements closest to the target base
            }
        }
        return true;
    }

    // Find Bel closest to a location, meeting chain requirements
    BelId find_closest_bel(float x, float y, int chain_size)
    {
        // TODO
        return BelId();
    }

    // Split a carry chain into multiple legal chains
    std::vector<CellChain> split_carry_chain(CellChain &carryc)
    {
        bool start_of_chain = true;
        std::vector<CellChain> chains;
        std::vector<const CellInfo *> tile;
        const int max_length = (ctx->chip_info->height - 2) * 8 - 2;
        auto curr_cell = carryc.cells.begin();
        while (curr_cell != carryc.cells.end()) {
            CellInfo *cell = *curr_cell;
            if (tile.size() >= 8) {
                tile.clear();
            }
            if (start_of_chain) {
                tile.clear();
                chains.emplace_back();
                start_of_chain = false;
                if (cell->ports.at(ctx->id("CIN")).net) {
                    // CIN is not constant and not part of a chain. Must feed in from fabric
                    CellInfo *feedin = make_carry_feed_in(cell, cell->ports.at(ctx->id("CIN")));
                    chains.back().cells.push_back(feedin);
                    tile.push_back(feedin);
                }
            }
            tile.push_back(cell);
            chains.back().cells.push_back(cell);
            bool split_chain = (!ctx->logicCellsCompatible(tile)) || (int(chains.back().cells.size()) > max_length);
            if (split_chain) {
                CellInfo *passout = make_carry_pass_out(cell->ports.at(ctx->id("COUT")));
                tile.pop_back();
                chains.back().cells.back() = passout;
                start_of_chain = true;
            } else {
                NetInfo *carry_net = cell->ports.at(ctx->id("COUT")).net;
                if (carry_net != nullptr && carry_net->users.size() > 1) {
                    CellInfo *passout = make_carry_pass_out(cell->ports.at(ctx->id("COUT")));
                    chains.back().cells.push_back(passout);
                    tile.push_back(passout);
                }
                ++curr_cell;
            }
        }
        return chains;
    }

    // Insert a logic cell to legalise a COUT->fabric connection
    CellInfo *make_carry_pass_out(PortInfo &cout_port)
    {
        assert(cout_port.net != nullptr);
        CellInfo *lc = create_ice_cell(ctx, ctx->id("ICESTORM_LC"));
        lc->params[ctx->id("LUT_INIT")] = "65280"; // 0xff00: O = I3
        lc->params[ctx->id("CARRY_ENABLE")] = "1";
        lc->ports.at(ctx->id("O")).net = cout_port.net;
        NetInfo *co_i3_net = new NetInfo();
        co_i3_net->name = ctx->id(lc->name.str(ctx) + "$I3");
        co_i3_net->driver = cout_port.net->driver;
        PortRef i3_r;
        i3_r.port = ctx->id("I3");
        i3_r.cell = lc;
        co_i3_net->users.push_back(i3_r);
        PortRef o_r;
        o_r.port = ctx->id("O");
        o_r.cell = lc;
        cout_port.net->driver = o_r;
        lc->ports.at(ctx->id("I3")).net = co_i3_net;
        // I1=1 feeds carry up the chain, so no need to actually break the chain
        lc->ports.at(ctx->id("I1")).net = ctx->nets.at(ctx->id("$PACKER_VCC_NET"));
        PortRef i1_r;
        i1_r.port = ctx->id("I1");
        i1_r.cell = lc;
        ctx->nets.at(ctx->id("$PACKER_VCC_NET"))->users.push_back(i1_r);
        ctx->cells[lc->name] = lc;
        createdCells.insert(lc->name);
        return lc;
    }

    // Insert a logic cell to legalise a CIN->fabric connection
    CellInfo *make_carry_feed_in(CellInfo *cin_cell, PortInfo &cin_port)
    {
        assert(cin_port.net != nullptr);
        CellInfo *lc = create_ice_cell(ctx, ctx->id("ICESTORM_LC"));
        lc->params[ctx->id("CARRY_ENABLE")] = "1";
        lc->params[ctx->id("CIN_CONST")] = "1";
        lc->params[ctx->id("CIN_SET")] = "1";
        lc->ports.at(ctx->id("I1")).net = cin_port.net;
        cin_port.net->users.erase(std::remove_if(cin_port.net->users.begin(), cin_port.net->users.end(),
                                                 [cin_cell, cin_port](const PortRef &usr) {
                                                     return usr.cell == cin_cell && usr.port == cin_port.name;
                                                 }));
        NetInfo *out_net = new NetInfo();
        out_net->name = ctx->id(lc->name.str(ctx) + "$O");

        ctx->cells[lc->name] = lc;
        createdCells.insert(lc->name);
        return lc;
    }

    Context *ctx;
    std::unordered_set<IdString> rippedCells;
    std::unordered_set<IdString> createdCells;
    // Go from X and Y position to logic cells, setting occupied to true if a Bel is unavailable
    std::vector<std::vector<std::vector<std::pair<BelId, bool>>>> logic_bels;
};

bool legalise_design(Context *ctx)
{
    PlacementLegaliser lg(ctx);
    return lg.legalise();
}

NEXTPNR_NAMESPACE_END
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2008 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Gabe Black
 */

#include "arch/x86/cpuid.hh"
#include "base/bitfield.hh"
#include "cpu/thread_context.hh"

namespace X86ISA {
    enum StandardCpuidFunction {
        VendorAndLargestStdFunc,
        FamilyModelStepping,
        NumStandardCpuidFuncs
    };

    enum ExtendedCpuidFunctions {
        VendorAndLargestExtFunc,
        FamilyModelSteppingBrandFeatures,
        NameString1,
        NameString2,
        NameString3,
        L1CacheAndTLB,
        L2L3CacheAndL2TLB,
        APMInfo,
        LongModeAddressSize,

        /*
         * The following are defined by the spec but not yet implemented
         */
/*      // Function 9 is reserved
        SVMInfo = 10,
        // Functions 11-24 are reserved
        TLB1GBPageInfo = 25,
        PerformanceInfo,*/

        NumExtendedCpuidFuncs
    };

    static const int vendorStringSize = 13;
    static const char vendorString[vendorStringSize] = "M5 Simulator";
    static const int nameStringSize = 48;
    static const char nameString[nameStringSize] = "Fake M5 x86_64 CPU";

    uint64_t
    stringToRegister(const char *str)
    {
        uint64_t reg = 0;
        for (int pos = 3; pos >=0; pos--) {
            reg <<= 8;
            reg |= str[pos];
        }
        return reg;
    }

    bool
    doCpuid(ThreadContext * tc, uint32_t function,
            uint32_t index, CpuidResult &result)
    {
        uint16_t family = bits(function, 31, 16);
        uint16_t funcNum = bits(function, 15, 0);
        if (family == 0x8000) {
            // The extended functions
            switch (funcNum) {
              case VendorAndLargestExtFunc:
                assert(vendorStringSize >= 12);
                result = CpuidResult(
                        0x80000000 + NumExtendedCpuidFuncs - 1,
                        stringToRegister(vendorString),
                        stringToRegister(vendorString + 4),
                        stringToRegister(vendorString + 8));
                break;
              case FamilyModelSteppingBrandFeatures:
                result = CpuidResult(0x00020f51, 0x00000405,
                                     0xe3d3fbff, 0x00000001);
                break;
              case NameString1:
              case NameString2:
              case NameString3:
                {
                    // Zero fill anything beyond the end of the string. This
                    // should go away once the string is a vetted parameter.
                    char cleanName[nameStringSize];
                    memset(cleanName, '\0', nameStringSize);
                    strncpy(cleanName, nameString, nameStringSize);

                    int offset = (funcNum - NameString1) * 16;
                    assert(nameStringSize >= offset + 16);
                    result = CpuidResult(
                            stringToRegister(cleanName + offset + 0),
                            stringToRegister(cleanName + offset + 4),
                            stringToRegister(cleanName + offset + 12),
                            stringToRegister(cleanName + offset + 8));
                }
                break;
              case L1CacheAndTLB:
                result = CpuidResult(0xff08ff08, 0xff20ff20,
                                     0x40020140, 0x40020140);
                break;
              case L2L3CacheAndL2TLB:
                result = CpuidResult(0x00000000, 0x42004200,
                                     0x00000000, 0x04008140);
                break;
              case APMInfo:
                result = CpuidResult(0x80000018, 0x68747541,
                                     0x69746e65, 0x444d4163);
                break;
              case LongModeAddressSize:
                result = CpuidResult(0x00003030, 0x00000000,
                                     0x00000000, 0x00000000);
                break;
/*            case SVMInfo:
              case TLB1GBPageInfo:
              case PerformanceInfo:*/
              default:
                warn("x86 cpuid: unimplemented function %u", funcNum);
                return false;
            }
        } else if(family == 0x0000) {
            // The standard functions
            switch (funcNum) {
              case VendorAndLargestStdFunc:
                assert(vendorStringSize >= 12);
                result = CpuidResult(
                        NumStandardCpuidFuncs - 1,
                        stringToRegister(vendorString),
                        stringToRegister(vendorString + 4),
                        stringToRegister(vendorString + 8));
                break;
              case FamilyModelStepping:
                result = CpuidResult(0x00020f51, 0x00000805,
                                     0xe7dbfbff, 0x00000001);
                break;
              default:
                warn("x86 cpuid: unimplemented function %u", funcNum);
                return false;
            }
        } else {
            warn("x86 cpuid: unknown family %#x", family);
            return false;
        }

        return true;
    }
} // namespace X86ISA
<commit_msg>x86: Enable three bits in the FamilyModelStepping ECX CPUID bitfield.<commit_after>/*
 * Copyright (c) 2008 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Gabe Black
 */

#include "arch/x86/cpuid.hh"
#include "base/bitfield.hh"
#include "cpu/thread_context.hh"

namespace X86ISA {
    enum StandardCpuidFunction {
        VendorAndLargestStdFunc,
        FamilyModelStepping,
        NumStandardCpuidFuncs
    };

    enum ExtendedCpuidFunctions {
        VendorAndLargestExtFunc,
        FamilyModelSteppingBrandFeatures,
        NameString1,
        NameString2,
        NameString3,
        L1CacheAndTLB,
        L2L3CacheAndL2TLB,
        APMInfo,
        LongModeAddressSize,

        /*
         * The following are defined by the spec but not yet implemented
         */
/*      // Function 9 is reserved
        SVMInfo = 10,
        // Functions 11-24 are reserved
        TLB1GBPageInfo = 25,
        PerformanceInfo,*/

        NumExtendedCpuidFuncs
    };

    static const int vendorStringSize = 13;
    static const char vendorString[vendorStringSize] = "M5 Simulator";
    static const int nameStringSize = 48;
    static const char nameString[nameStringSize] = "Fake M5 x86_64 CPU";

    uint64_t
    stringToRegister(const char *str)
    {
        uint64_t reg = 0;
        for (int pos = 3; pos >=0; pos--) {
            reg <<= 8;
            reg |= str[pos];
        }
        return reg;
    }

    bool
    doCpuid(ThreadContext * tc, uint32_t function,
            uint32_t index, CpuidResult &result)
    {
        uint16_t family = bits(function, 31, 16);
        uint16_t funcNum = bits(function, 15, 0);
        if (family == 0x8000) {
            // The extended functions
            switch (funcNum) {
              case VendorAndLargestExtFunc:
                assert(vendorStringSize >= 12);
                result = CpuidResult(
                        0x80000000 + NumExtendedCpuidFuncs - 1,
                        stringToRegister(vendorString),
                        stringToRegister(vendorString + 4),
                        stringToRegister(vendorString + 8));
                break;
              case FamilyModelSteppingBrandFeatures:
                result = CpuidResult(0x00020f51, 0x00000405,
                                     0xe3d3fbff, 0x00000001);
                break;
              case NameString1:
              case NameString2:
              case NameString3:
                {
                    // Zero fill anything beyond the end of the string. This
                    // should go away once the string is a vetted parameter.
                    char cleanName[nameStringSize];
                    memset(cleanName, '\0', nameStringSize);
                    strncpy(cleanName, nameString, nameStringSize);

                    int offset = (funcNum - NameString1) * 16;
                    assert(nameStringSize >= offset + 16);
                    result = CpuidResult(
                            stringToRegister(cleanName + offset + 0),
                            stringToRegister(cleanName + offset + 4),
                            stringToRegister(cleanName + offset + 12),
                            stringToRegister(cleanName + offset + 8));
                }
                break;
              case L1CacheAndTLB:
                result = CpuidResult(0xff08ff08, 0xff20ff20,
                                     0x40020140, 0x40020140);
                break;
              case L2L3CacheAndL2TLB:
                result = CpuidResult(0x00000000, 0x42004200,
                                     0x00000000, 0x04008140);
                break;
              case APMInfo:
                result = CpuidResult(0x80000018, 0x68747541,
                                     0x69746e65, 0x444d4163);
                break;
              case LongModeAddressSize:
                result = CpuidResult(0x00003030, 0x00000000,
                                     0x00000000, 0x00000000);
                break;
/*            case SVMInfo:
              case TLB1GBPageInfo:
              case PerformanceInfo:*/
              default:
                warn("x86 cpuid: unimplemented function %u", funcNum);
                return false;
            }
        } else if(family == 0x0000) {
            // The standard functions
            switch (funcNum) {
              case VendorAndLargestStdFunc:
                assert(vendorStringSize >= 12);
                result = CpuidResult(
                        NumStandardCpuidFuncs - 1,
                        stringToRegister(vendorString),
                        stringToRegister(vendorString + 4),
                        stringToRegister(vendorString + 8));
                break;
              case FamilyModelStepping:
                result = CpuidResult(0x00020f51, 0x00000805,
                                     0xe7dbfbff, 0x04000209);
                break;
              default:
                warn("x86 cpuid: unimplemented function %u", funcNum);
                return false;
            }
        } else {
            warn("x86 cpuid: unknown family %#x", family);
            return false;
        }

        return true;
    }
} // namespace X86ISA
<|endoftext|>


<commit_before>// Copyright 2010 Alessio Sclocco <a.sclocco@vu.nl>
//
// 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.

#include <string>
#include <list>
#include <exception>
#include <typeinfo>

#include <utils.hpp>

#ifndef ARGUMENT_LIST_HPP
#define ARGUMENT_LIST_HPP

namespace isa {
namespace utils {

// Exception: no items in the command line
class EmptyCommandLine : public std::exception {};
// Exception: requested switch not present
class SwitchNotFound : public std::exception {
public:
  SwitchNotFound(std::string option);
  ~SwitchNotFound() throw ();

  const char * what() const throw ();

private:
  std::string option;
};

// ArgumentList class
class ArgumentList {
public:
	ArgumentList(int argc, char * argv[]);
	~ArgumentList();

  std::string getName();
	template< typename T > T getFirst() throw(EmptyCommandLine);
	bool getSwitch(const std::string & option) throw(EmptyCommandLine);
	template< typename T > T getSwitchArgument(const std::string & option) throw(EmptyCommandLine, SwitchNotFound);

private:
  std::list< std::string > args;
  std::string name;
};

// Implementations

template< typename T > T ArgumentList::getFirst() throw(EmptyCommandLine) {
	if ( args.empty() ) {
		throw EmptyCommandLine();
	}

  std::string temp = args.front();
	args.pop_front();
	return castToType< std::string, T >(temp);
}

template< class T > T ArgumentList::getSwitchArgument(const std::string & option) throw(EmptyCommandLine, SwitchNotFound) {
	if ( args.empty() ) {
		throw EmptyCommandLine();
	}

	for ( std::list< std::string >::iterator s = args.begin(); s != args.end(); ++s ) {
		if ( option.compare(*s) == 0 ) {
      std::string temp = *(++s);
			T retVal = castToType< std::string, T >(temp);

			args.erase(--s);
			args.erase(s);
			return retVal;
		}
	}

	throw SwitchNotFound(option);
}

inline std::string ArgumentList::getName() {
	return name;
}

} // utils
} // isa

#endif // ARGUMENT_LIST_HPP

<commit_msg>Cleaning the code of getSwitchArgument.<commit_after>// Copyright 2010 Alessio Sclocco <a.sclocco@vu.nl>
//
// 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.

#include <string>
#include <list>
#include <iterator>
#include <exception>
#include <typeinfo>

#include <utils.hpp>

#ifndef ARGUMENT_LIST_HPP
#define ARGUMENT_LIST_HPP

namespace isa {
namespace utils {

// Exception: no items in the command line
class EmptyCommandLine : public std::exception {};
// Exception: requested switch not present
class SwitchNotFound : public std::exception {
public:
  SwitchNotFound(std::string option);
  ~SwitchNotFound() throw ();

  const char * what() const throw ();

private:
  std::string option;
};

// ArgumentList class
class ArgumentList {
public:
	ArgumentList(int argc, char * argv[]);
	~ArgumentList();

  std::string getName();
	template< typename T > T getFirst() throw(EmptyCommandLine);
	bool getSwitch(const std::string & option) throw(EmptyCommandLine);
	template< typename T > T getSwitchArgument(const std::string & option) throw(EmptyCommandLine, SwitchNotFound);

private:
  std::list< std::string > args;
  std::string name;
};

// Implementations

template< typename T > T ArgumentList::getFirst() throw(EmptyCommandLine) {
	if ( args.empty() ) {
		throw EmptyCommandLine();
	}

  std::string temp = args.front();
	args.pop_front();
	return castToType< std::string, T >(temp);
}

template< class T > T ArgumentList::getSwitchArgument(const std::string & option) throw(EmptyCommandLine, SwitchNotFound) {
	if ( args.empty() ) {
		throw EmptyCommandLine();
	}

	for ( std::list< std::string >::iterator item = args.begin(); item != args.end(); ++item ) {
    std::list< std::string >::iterator next = std::advance(item, 1);

		if ( option.compare(*item) == 0 ) {
			T retVal = castToType< std::string, T >(*next);

      std::advance(next, 1);
      args.erase(item, next);
			return retVal;
		}
	}

	throw SwitchNotFound(option);
}

inline std::string ArgumentList::getName() {
	return name;
}

} // utils
} // isa

#endif // ARGUMENT_LIST_HPP

<|endoftext|>


<commit_before>#include "plot_view.hpp"
#include "plot.hpp"
#include "../utility/vector.hpp"

#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QPainter>
#include <QDebug>
#include <QCursor>
#include <QMouseEvent>
#include <QWheelEvent>
#include <algorithm>
#include <iostream>
#include <iterator>

using namespace std;

namespace datavis {

PlotView::PlotView(QWidget * parent):
    QWidget(parent)
{
    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);

    m_canvas = new PlotCanvas;
    m_canvas->setMouseTracking(true);

    auto layout = new QVBoxLayout(this);
    layout->addWidget(m_canvas);
}

void PlotView::addPlot(Plot * plot)
{
    if (!plot)
        return;

    plot->setView(this);

    m_canvas->m_plots.push_back(plot);

    connect(plot, &Plot::xRangeChanged,
            this, &PlotView::onPlotRangeChanged);
    connect(plot, &Plot::yRangeChanged,
            this, &PlotView::onPlotRangeChanged);
    connect(plot, &Plot::contentChanged,
            this, &PlotView::onPlotContentChanged);

    m_canvas->updateDataRange();

    m_canvas->update();
}

void PlotView::removePlot(Plot * plot)
{
    if (!plot)
        return;

    auto & plots = m_canvas->m_plots;

    auto handle = std::find(plots.begin(), plots.end(), plot);
    if (handle != plots.end())
    {
        disconnect(plot, 0, this, 0);
        m_canvas->m_plots.erase(handle);
    }

    m_canvas->updateDataRange();

    m_canvas->update();
}

void PlotView::setStacked(bool value)
{
    m_canvas->m_stacked = value;
    m_canvas->update();
}

void PlotView::setCommonX(bool value)
{
    m_canvas->m_common_x = value;
    m_canvas->update();
}

void PlotView::setCommonY(bool value)
{
    m_canvas->m_common_y = value;
    m_canvas->update();
}

void PlotView::onPlotRangeChanged()
{
    m_canvas->updateDataRange();
    m_canvas->update();
}

void PlotView::onPlotContentChanged()
{
    m_canvas->update();
}

QRect PlotCanvas::plotRect(int index)
{
    int plot_count = (int) m_plots.size();

    double plot_width = width() - 2 * m_margin;

    int plot_left = m_margin;
    int plot_right = width() - m_margin;

    int plot_top;
    int plot_bottom;

    if (m_stacked)
    {
        plot_top = m_margin;
        plot_bottom = height() - m_margin;
    }
    else
    {
        double plot_offset = double(height() - m_margin) / plot_count;
        plot_top = m_margin + index * plot_offset;
        plot_bottom = (index + 1) * plot_offset;
    }

    return QRect(QPoint(plot_left, plot_top), QPoint(plot_right, plot_bottom));
}

QPointF PlotCanvas::mapToPlot(int plotIndex, const QPointF & pos)
{
    if (m_plots.empty())
        return pos;

    auto plot = m_plots[plotIndex];

    auto xRange = m_common_x ? view_x_range : plot->xRange();
    auto yRange = m_common_y ? total_y_range : plot->yRange();

    auto rect = plotRect(plotIndex);
    if (rect.isEmpty())
        return QPointF(0,0);

    double x = (pos.x() - rect.x()) * (xRange.extent() / rect.width()) + xRange.min;
    double y = (rect.y() + rect.height() - pos.y()) * (yRange.extent() / rect.height()) + yRange.min;

    return QPointF(x,y);
}

void PlotCanvas::updateDataRange()
{
    bool first = true;
    for (auto plot : m_plots)
    {
        if (plot->isEmpty())
            continue;

        auto x_range = plot->xRange();
        auto y_range = plot->yRange();

        if (first)
        {
            total_x_range = x_range;
            total_y_range = y_range;
        }
        else
        {
            total_x_range.min = min(total_x_range.min, x_range.min);
            total_x_range.max = max(total_x_range.max, x_range.max);

            total_y_range.min = min(total_y_range.min, y_range.min);
            total_y_range.max = max(total_y_range.max, y_range.max);
        }
        first = false;
    }

    // Reset view
    view_x_range = total_x_range;
}

void PlotCanvas::resizeEvent(QResizeEvent*)
{
}

void PlotCanvas::enterEvent(QEvent*)
{
    update();
}

void PlotCanvas::leaveEvent(QEvent*)
{
    update();
}

void PlotCanvas::mouseMoveEvent(QMouseEvent*)
{
    update();
}

void PlotCanvas::mousePressEvent(QMouseEvent* event)
{
    auto pos = event->pos();
    for (int i = 0; i < m_plots.size(); ++i)
    {
        auto plot = m_plots[i];
        if (plotRect(i).contains(pos))
        {
            auto plotPos = mapToPlot(i, pos);
            auto dataPos = plot->dataLocation(plotPos);
            auto dataIndex = plot->dataSet()->indexForPoint(dataPos);
            plot->dataSet()->selectIndex(dataIndex);
            return;
        }
    }
}

void PlotCanvas::wheelEvent(QWheelEvent* event)
{
    if (event->phase() != Qt::ScrollUpdate)
        return;

    double degrees = event->angleDelta().y() / 8.0;

    if (event->modifiers() & Qt::ControlModifier)
    {
        double new_size = view_x_range.extent() * pow(2.0, -degrees/90.0);
        //qDebug() << "new size = " << new_size;
        setSize(new_size);
    }
    else if (event->modifiers() & Qt::ShiftModifier)
    {
        double new_offset = view_x_range.min + view_x_range.extent() * degrees/180.0;
        //qDebug() << "new offset = " << new_offset;
        setOffset(new_offset);
    }
}

void PlotCanvas::paintEvent(QPaintEvent* event)
{
    auto mouse_pos = mapFromGlobal(QCursor::pos());

    QPainter painter(this);

    painter.fillRect(rect(), Qt::white);

    if (m_plots.empty())
        return;

    painter.setBrush(Qt::NoBrush);

    QPen frame_pen;
    frame_pen.setColor(Qt::lightGray);

    int plot_index = 0;

    int plot_under_mouse_index = -1;

    for (auto plot : m_plots)
    {
        auto plot_rect = this->plotRect(plot_index);

        if (plot_rect.contains(mouse_pos))
            plot_under_mouse_index = plot_index;

        painter.setPen(frame_pen);
        painter.drawRect(plot_rect);

        if (plot->isEmpty())
            continue;

        Mapping2d map;

        Plot::Range x_range = m_common_x ? view_x_range : plot->xRange();
        Plot::Range y_range = m_common_y ? total_y_range : plot->yRange();

        QRectF region(x_range.min, y_range.min, x_range.extent(), y_range.extent());

        double x_extent = x_range.extent();
        double y_extent = y_range.extent();
        double x_scale = x_extent == 0 ? 1 : 1.0 / x_extent;
        double y_scale = y_extent == 0 ? 1 : 1.0 / y_extent;

        map.translate(-x_range.min, -y_range.min);
        map.scale(x_scale, y_scale);
        map.scale(plot_rect.width(), -plot_rect.height());
        map.translate(plot_rect.x(), plot_rect.y() + plot_rect.height());

        painter.setClipRect(plot_rect);

        painter.save();

        plot->plot(&painter, map, region);

        painter.restore();

        ++plot_index;
    }

    painter.setClipping(false);

    if (underMouse())
    {
        auto pos = mapFromGlobal(QCursor::pos());

        QPen cursor_pen;
        cursor_pen.setColor(Qt::red);
        cursor_pen.setWidth(1);

        painter.setPen(cursor_pen);

        painter.drawLine(pos.x(), 0, pos.x(), height());
        painter.drawLine(0, pos.y(), width(), pos.y());

        if (plot_under_mouse_index >= 0)
        {
            auto plot = m_plots[plot_under_mouse_index];
            auto plotPos = mapToPlot(plot_under_mouse_index, pos);
            auto dataPos = plot->dataLocation(plotPos);
            auto dataIndex = plot->dataSet()->indexForPoint(dataPos);

            vector<int> dataSize = plot->dataSet()->data()->size();

            bool in_bounds = true;
            for (int d = 0; d < dataSize.size(); ++d)
                in_bounds &= (dataIndex[d] >= 0 && dataIndex[d] < dataSize[d]);

            QString text;

            if (in_bounds)
            {
                double value = (*plot->dataSet()->data())(dataIndex);

                text += QString::number(value);
                text += " ";
            }

            QStringList coord_strings;
            for (auto & i : dataPos)
                coord_strings << QString::number(i, 'f', 2);

            text += "@ ";
            text += coord_strings.join(" ");

            auto fm = fontMetrics();
            auto rect = fm.boundingRect(text);
            rect.setWidth(rect.width() + 10);

            int x, y;

            if (pos.x() < width() / 2)
                x = pos.x() + 20;
            else
                x = pos.x() - 20 - rect.width();

            if (pos.y() < height() / 2)
                y = pos.y() + 20 + fm.ascent();
            else
                y = pos.y() - 20 - fm.descent();

            rect.translate(x, y);

            painter.fillRect(rect.adjusted(-2,0,2,0), QColor(255,255,255,230));

            painter.drawText(QPoint(x, y), text);
        }
    }
}

Plot * PlotView::plotAt(const QPoint & view_pos)
{
    QPoint pos = m_canvas->mapFrom(this, view_pos);

    if (m_canvas->m_plots.empty())
        return nullptr;

    if (m_canvas->m_stacked)
        return nullptr;

    int plot_count = (int) m_canvas->m_plots.size();
    float plot_height = float(m_canvas->height()) / plot_count;
    int plot_index = float(pos.y()) / plot_height;
    if (plot_index < 0 || plot_index >= plot_count)
        return nullptr;

    auto iter = m_canvas->m_plots.begin();
    advance(iter, plot_index);
    return *iter;
}

double PlotCanvas::position()
{
    double data_extent = total_x_range.extent();
    double offset = view_x_range.min - total_x_range.min;
    if (data_extent > 0)
        return offset / data_extent;
    else
        return 0;
}

void PlotCanvas::setPosition(double value)
{
    double extent = total_x_range.extent();
    double offset = value * extent + total_x_range.min;
    setOffset(offset);
}

void PlotCanvas::setOffset(double offset)
{
    double extent = view_x_range.extent();
    double max_offset = total_x_range.max - extent;
    offset = std::max(total_x_range.min, offset);
    offset = std::min(max_offset, offset);

    if (offset == view_x_range.min)
        return;

    view_x_range.min = offset;
    view_x_range.max = offset + extent;

    update();
}

double PlotCanvas::range()
{
    double data_extent = total_x_range.extent();
    double view_extent = view_x_range.extent();
    if (data_extent > 0)
        return view_extent / data_extent;
    else
        return 0;
}

void PlotCanvas::setRange(double value)
{
    double extent = total_x_range.extent();
    double size = value * extent;
    setSize(size);
}

void PlotCanvas::setSize(double size)
{
    double extent = total_x_range.extent();

    size = std::min(extent, size);
    size = std::max(0.0, size);

    if (size == view_x_range.extent())
        return;

    double max_offset = total_x_range.max - size;
    view_x_range.min = std::min(view_x_range.min, max_offset);
    view_x_range.max = view_x_range.min + size;

    update();
}

}
<commit_msg>Slow down zoom/scroll interaction with mouse wheel<commit_after>#include "plot_view.hpp"
#include "plot.hpp"
#include "../utility/vector.hpp"

#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QPainter>
#include <QDebug>
#include <QCursor>
#include <QMouseEvent>
#include <QWheelEvent>
#include <algorithm>
#include <iostream>
#include <iterator>

using namespace std;

namespace datavis {

PlotView::PlotView(QWidget * parent):
    QWidget(parent)
{
    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);

    m_canvas = new PlotCanvas;
    m_canvas->setMouseTracking(true);

    auto layout = new QVBoxLayout(this);
    layout->addWidget(m_canvas);
}

void PlotView::addPlot(Plot * plot)
{
    if (!plot)
        return;

    plot->setView(this);

    m_canvas->m_plots.push_back(plot);

    connect(plot, &Plot::xRangeChanged,
            this, &PlotView::onPlotRangeChanged);
    connect(plot, &Plot::yRangeChanged,
            this, &PlotView::onPlotRangeChanged);
    connect(plot, &Plot::contentChanged,
            this, &PlotView::onPlotContentChanged);

    m_canvas->updateDataRange();

    m_canvas->update();
}

void PlotView::removePlot(Plot * plot)
{
    if (!plot)
        return;

    auto & plots = m_canvas->m_plots;

    auto handle = std::find(plots.begin(), plots.end(), plot);
    if (handle != plots.end())
    {
        disconnect(plot, 0, this, 0);
        m_canvas->m_plots.erase(handle);
    }

    m_canvas->updateDataRange();

    m_canvas->update();
}

void PlotView::setStacked(bool value)
{
    m_canvas->m_stacked = value;
    m_canvas->update();
}

void PlotView::setCommonX(bool value)
{
    m_canvas->m_common_x = value;
    m_canvas->update();
}

void PlotView::setCommonY(bool value)
{
    m_canvas->m_common_y = value;
    m_canvas->update();
}

void PlotView::onPlotRangeChanged()
{
    m_canvas->updateDataRange();
    m_canvas->update();
}

void PlotView::onPlotContentChanged()
{
    m_canvas->update();
}

QRect PlotCanvas::plotRect(int index)
{
    int plot_count = (int) m_plots.size();

    double plot_width = width() - 2 * m_margin;

    int plot_left = m_margin;
    int plot_right = width() - m_margin;

    int plot_top;
    int plot_bottom;

    if (m_stacked)
    {
        plot_top = m_margin;
        plot_bottom = height() - m_margin;
    }
    else
    {
        double plot_offset = double(height() - m_margin) / plot_count;
        plot_top = m_margin + index * plot_offset;
        plot_bottom = (index + 1) * plot_offset;
    }

    return QRect(QPoint(plot_left, plot_top), QPoint(plot_right, plot_bottom));
}

QPointF PlotCanvas::mapToPlot(int plotIndex, const QPointF & pos)
{
    if (m_plots.empty())
        return pos;

    auto plot = m_plots[plotIndex];

    auto xRange = m_common_x ? view_x_range : plot->xRange();
    auto yRange = m_common_y ? total_y_range : plot->yRange();

    auto rect = plotRect(plotIndex);
    if (rect.isEmpty())
        return QPointF(0,0);

    double x = (pos.x() - rect.x()) * (xRange.extent() / rect.width()) + xRange.min;
    double y = (rect.y() + rect.height() - pos.y()) * (yRange.extent() / rect.height()) + yRange.min;

    return QPointF(x,y);
}

void PlotCanvas::updateDataRange()
{
    bool first = true;
    for (auto plot : m_plots)
    {
        if (plot->isEmpty())
            continue;

        auto x_range = plot->xRange();
        auto y_range = plot->yRange();

        if (first)
        {
            total_x_range = x_range;
            total_y_range = y_range;
        }
        else
        {
            total_x_range.min = min(total_x_range.min, x_range.min);
            total_x_range.max = max(total_x_range.max, x_range.max);

            total_y_range.min = min(total_y_range.min, y_range.min);
            total_y_range.max = max(total_y_range.max, y_range.max);
        }
        first = false;
    }

    // Reset view
    view_x_range = total_x_range;
}

void PlotCanvas::resizeEvent(QResizeEvent*)
{
}

void PlotCanvas::enterEvent(QEvent*)
{
    update();
}

void PlotCanvas::leaveEvent(QEvent*)
{
    update();
}

void PlotCanvas::mouseMoveEvent(QMouseEvent*)
{
    update();
}

void PlotCanvas::mousePressEvent(QMouseEvent* event)
{
    auto pos = event->pos();
    for (int i = 0; i < m_plots.size(); ++i)
    {
        auto plot = m_plots[i];
        if (plotRect(i).contains(pos))
        {
            auto plotPos = mapToPlot(i, pos);
            auto dataPos = plot->dataLocation(plotPos);
            auto dataIndex = plot->dataSet()->indexForPoint(dataPos);
            plot->dataSet()->selectIndex(dataIndex);
            return;
        }
    }
}

void PlotCanvas::wheelEvent(QWheelEvent* event)
{
    if (event->phase() != Qt::ScrollUpdate)
        return;

    double degrees = event->angleDelta().y() / 8.0;

    if (event->modifiers() & Qt::ControlModifier)
    {
        double new_size = view_x_range.extent() * pow(2.0, -1.0 * degrees/360.0);
        //qDebug() << "new size = " << new_size;
        setSize(new_size);
    }
    else if (event->modifiers() & Qt::ShiftModifier)
    {
        double new_offset = view_x_range.min + 0.5 * view_x_range.extent() * degrees/360.0;
        //qDebug() << "new offset = " << new_offset;
        setOffset(new_offset);
    }
}

void PlotCanvas::paintEvent(QPaintEvent* event)
{
    auto mouse_pos = mapFromGlobal(QCursor::pos());

    QPainter painter(this);

    painter.fillRect(rect(), Qt::white);

    if (m_plots.empty())
        return;

    painter.setBrush(Qt::NoBrush);

    QPen frame_pen;
    frame_pen.setColor(Qt::lightGray);

    int plot_index = 0;

    int plot_under_mouse_index = -1;

    for (auto plot : m_plots)
    {
        auto plot_rect = this->plotRect(plot_index);

        if (plot_rect.contains(mouse_pos))
            plot_under_mouse_index = plot_index;

        painter.setPen(frame_pen);
        painter.drawRect(plot_rect);

        if (plot->isEmpty())
            continue;

        Mapping2d map;

        Plot::Range x_range = m_common_x ? view_x_range : plot->xRange();
        Plot::Range y_range = m_common_y ? total_y_range : plot->yRange();

        QRectF region(x_range.min, y_range.min, x_range.extent(), y_range.extent());

        double x_extent = x_range.extent();
        double y_extent = y_range.extent();
        double x_scale = x_extent == 0 ? 1 : 1.0 / x_extent;
        double y_scale = y_extent == 0 ? 1 : 1.0 / y_extent;

        map.translate(-x_range.min, -y_range.min);
        map.scale(x_scale, y_scale);
        map.scale(plot_rect.width(), -plot_rect.height());
        map.translate(plot_rect.x(), plot_rect.y() + plot_rect.height());

        painter.setClipRect(plot_rect);

        painter.save();

        plot->plot(&painter, map, region);

        painter.restore();

        ++plot_index;
    }

    painter.setClipping(false);

    if (underMouse())
    {
        auto pos = mapFromGlobal(QCursor::pos());

        QPen cursor_pen;
        cursor_pen.setColor(Qt::red);
        cursor_pen.setWidth(1);

        painter.setPen(cursor_pen);

        painter.drawLine(pos.x(), 0, pos.x(), height());
        painter.drawLine(0, pos.y(), width(), pos.y());

        if (plot_under_mouse_index >= 0)
        {
            auto plot = m_plots[plot_under_mouse_index];
            auto plotPos = mapToPlot(plot_under_mouse_index, pos);
            auto dataPos = plot->dataLocation(plotPos);
            auto dataIndex = plot->dataSet()->indexForPoint(dataPos);

            vector<int> dataSize = plot->dataSet()->data()->size();

            bool in_bounds = true;
            for (int d = 0; d < dataSize.size(); ++d)
                in_bounds &= (dataIndex[d] >= 0 && dataIndex[d] < dataSize[d]);

            QString text;

            if (in_bounds)
            {
                double value = (*plot->dataSet()->data())(dataIndex);

                text += QString::number(value);
                text += " ";
            }

            QStringList coord_strings;
            for (auto & i : dataPos)
                coord_strings << QString::number(i, 'f', 2);

            text += "@ ";
            text += coord_strings.join(" ");

            auto fm = fontMetrics();
            auto rect = fm.boundingRect(text);
            rect.setWidth(rect.width() + 10);

            int x, y;

            if (pos.x() < width() / 2)
                x = pos.x() + 20;
            else
                x = pos.x() - 20 - rect.width();

            if (pos.y() < height() / 2)
                y = pos.y() + 20 + fm.ascent();
            else
                y = pos.y() - 20 - fm.descent();

            rect.translate(x, y);

            painter.fillRect(rect.adjusted(-2,0,2,0), QColor(255,255,255,230));

            painter.drawText(QPoint(x, y), text);
        }
    }
}

Plot * PlotView::plotAt(const QPoint & view_pos)
{
    QPoint pos = m_canvas->mapFrom(this, view_pos);

    if (m_canvas->m_plots.empty())
        return nullptr;

    if (m_canvas->m_stacked)
        return nullptr;

    int plot_count = (int) m_canvas->m_plots.size();
    float plot_height = float(m_canvas->height()) / plot_count;
    int plot_index = float(pos.y()) / plot_height;
    if (plot_index < 0 || plot_index >= plot_count)
        return nullptr;

    auto iter = m_canvas->m_plots.begin();
    advance(iter, plot_index);
    return *iter;
}

double PlotCanvas::position()
{
    double data_extent = total_x_range.extent();
    double offset = view_x_range.min - total_x_range.min;
    if (data_extent > 0)
        return offset / data_extent;
    else
        return 0;
}

void PlotCanvas::setPosition(double value)
{
    double extent = total_x_range.extent();
    double offset = value * extent + total_x_range.min;
    setOffset(offset);
}

void PlotCanvas::setOffset(double offset)
{
    double extent = view_x_range.extent();
    double max_offset = total_x_range.max - extent;
    offset = std::max(total_x_range.min, offset);
    offset = std::min(max_offset, offset);

    if (offset == view_x_range.min)
        return;

    view_x_range.min = offset;
    view_x_range.max = offset + extent;

    update();
}

double PlotCanvas::range()
{
    double data_extent = total_x_range.extent();
    double view_extent = view_x_range.extent();
    if (data_extent > 0)
        return view_extent / data_extent;
    else
        return 0;
}

void PlotCanvas::setRange(double value)
{
    double extent = total_x_range.extent();
    double size = value * extent;
    setSize(size);
}

void PlotCanvas::setSize(double size)
{
    double extent = total_x_range.extent();

    size = std::min(extent, size);
    size = std::max(0.0, size);

    if (size == view_x_range.extent())
        return;

    double max_offset = total_x_range.max - size;
    view_x_range.min = std::min(view_x_range.min, max_offset);
    view_x_range.max = view_x_range.min + size;

    update();
}

}
<|endoftext|>


<commit_before>#include "parser.hpp"

#include <iostream>

#include <clang-c/Index.h>

#include "parser.class.hpp"
#include "parser.enum.hpp"
#include "parser.function.hpp"
#include "parser.util.hpp"

using namespace reflang;
using namespace std;

namespace
{
	ostream& operator<<(ostream& s, const CXString& str)
	{
		s << parser::Convert(str);
		return s;
	}

	CXTranslationUnit Parse(CXIndex& index, int argc, char* argv[])
	{
		CXTranslationUnit unit;
		auto error = clang_parseTranslationUnit2(
				index, 0, argv, argc, 0, 0, CXTranslationUnit_None, &unit);
		if (error != 0)
		{
			cerr << "Unable to parse translation unit. Quitting." << endl;
			exit(-1);
		}

		auto diagnostics = clang_getNumDiagnostics(unit);
		if (diagnostics != 0)
		{
			cerr << "> Diagnostics:" << endl;
			for (int i = 0; i != diagnostics; ++i)
			{
				auto diag = clang_getDiagnostic(unit, i);
				cerr << ">>> "
					<< clang_formatDiagnostic(
							diag, clang_defaultDiagnosticDisplayOptions());
			}
		}

		return unit;
	}

	struct GetSupportedTypeNamesStruct
	{
		vector<string>* results;
		const parser::Options* options;
	};

	CXChildVisitResult GetSupportedTypeNamesVisitor(
			CXCursor cursor, CXCursor parent, CXClientData client_data)
	{
		auto* data = reinterpret_cast<GetSupportedTypeNamesStruct*>(
				client_data);
		if (clang_getCursorKind(cursor) == CXCursor_EnumDecl)
		{
			string name = parser::GetFullName(cursor);
			if (regex_match(name, data->options->include) &&
					!regex_match(name, data->options->exclude))
			{
				data->results->push_back(name);
			}
		}
		return CXChildVisit_Recurse;
	}

	struct GetTypesStruct
	{
		vector<unique_ptr<TypeBase>>* types;
		const parser::Options* options;
	};

	CXChildVisitResult GetTypesVisitor(
			CXCursor cursor, CXCursor parent, CXClientData client_data)
	{
		auto* data = reinterpret_cast<GetTypesStruct*>(client_data);
		std::unique_ptr<TypeBase> type;
		switch (clang_getCursorKind(cursor))
		{
			case CXCursor_EnumDecl:
				type = std::make_unique<Enum>(parser::GetEnum(cursor));
				break;
			case CXCursor_ClassDecl:
				type = std::make_unique<Class>(parser::GetClass(cursor));
				break;
			case CXCursor_FunctionDecl:
				type = std::make_unique<Function>(parser::GetFunction(cursor));
				break;
			default:
				break;
		}

		const string& name = type->GetFullName();
		if (type
				&& !name.empty()
				&& !(name.back() == ':')
				&& regex_match(name, data->options->include)
				&& !regex_match(name, data->options->exclude))
		{
			data->types->push_back(std::move(type));
		}

		return CXChildVisit_Recurse;
	}
}  // namespace

vector<string> parser::GetSupportedTypeNames(
		int argc, char* argv[], const Options& options)
{
	CXIndex index = clang_createIndex(0, 0);
	CXTranslationUnit unit = Parse(index, argc, argv);

	auto cursor = clang_getTranslationUnitCursor(unit);

	vector<string> results;
	GetSupportedTypeNamesStruct data = { &results, &options };
	clang_visitChildren(cursor, GetSupportedTypeNamesVisitor, &data);

	clang_disposeTranslationUnit(unit);
	clang_disposeIndex(index);
	return results;
}

vector<unique_ptr<TypeBase>> parser::GetTypes(
		int argc, char* argv[], const Options& options)
{
	CXIndex index = clang_createIndex(0, 0);
	CXTranslationUnit unit = Parse(index, argc, argv);

	auto cursor = clang_getTranslationUnitCursor(unit);

	vector<unique_ptr<TypeBase>> results;
	GetTypesStruct data = { &results, &options };
	clang_visitChildren(cursor, GetTypesVisitor, &data);

	clang_disposeTranslationUnit(unit);
	clang_disposeIndex(index);
	return results;
}
<commit_msg>Fix bug: --list-only only supported enums. Now it supports all types.<commit_after>#include "parser.hpp"

#include <iostream>

#include <clang-c/Index.h>

#include "parser.class.hpp"
#include "parser.enum.hpp"
#include "parser.function.hpp"
#include "parser.util.hpp"

using namespace reflang;
using namespace std;

namespace
{
	ostream& operator<<(ostream& s, const CXString& str)
	{
		s << parser::Convert(str);
		return s;
	}

	CXTranslationUnit Parse(CXIndex& index, int argc, char* argv[])
	{
		CXTranslationUnit unit;
		auto error = clang_parseTranslationUnit2(
				index, 0, argv, argc, 0, 0, CXTranslationUnit_None, &unit);
		if (error != 0)
		{
			cerr << "Unable to parse translation unit. Quitting." << endl;
			exit(-1);
		}

		auto diagnostics = clang_getNumDiagnostics(unit);
		if (diagnostics != 0)
		{
			cerr << "> Diagnostics:" << endl;
			for (int i = 0; i != diagnostics; ++i)
			{
				auto diag = clang_getDiagnostic(unit, i);
				cerr << ">>> "
					<< clang_formatDiagnostic(
							diag, clang_defaultDiagnosticDisplayOptions());
			}
		}

		return unit;
	}

	struct GetTypesStruct
	{
		vector<unique_ptr<TypeBase>>* types;
		const parser::Options* options;
	};

	CXChildVisitResult GetTypesVisitor(
			CXCursor cursor, CXCursor parent, CXClientData client_data)
	{
		auto* data = reinterpret_cast<GetTypesStruct*>(client_data);
		std::unique_ptr<TypeBase> type;
		switch (clang_getCursorKind(cursor))
		{
			case CXCursor_EnumDecl:
				type = std::make_unique<Enum>(parser::GetEnum(cursor));
				break;
			case CXCursor_ClassDecl:
				type = std::make_unique<Class>(parser::GetClass(cursor));
				break;
			case CXCursor_FunctionDecl:
				type = std::make_unique<Function>(parser::GetFunction(cursor));
				break;
			default:
				break;
		}

		const string& name = type->GetFullName();
		if (type
				&& !name.empty()
				&& !(name.back() == ':')
				&& regex_match(name, data->options->include)
				&& !regex_match(name, data->options->exclude))
		{
			data->types->push_back(std::move(type));
		}

		return CXChildVisit_Recurse;
	}
}  // namespace

vector<string> parser::GetSupportedTypeNames(
		int argc, char* argv[], const Options& options)
{
	auto types = GetTypes(argc, argv, options);

	vector<string> names;
	names.reserve(types.size());
	for (const auto& type : types)
	{
		names.push_back(type->GetFullName());
	}
	return names;
}

vector<unique_ptr<TypeBase>> parser::GetTypes(
		int argc, char* argv[], const Options& options)
{
	CXIndex index = clang_createIndex(0, 0);
	CXTranslationUnit unit = Parse(index, argc, argv);

	auto cursor = clang_getTranslationUnitCursor(unit);

	vector<unique_ptr<TypeBase>> results;
	GetTypesStruct data = { &results, &options };
	clang_visitChildren(cursor, GetTypesVisitor, &data);

	clang_disposeTranslationUnit(unit);
	clang_disposeIndex(index);
	return results;
}
<|endoftext|>


<commit_before>//=======================================================================
// Copyright (c) 2014 Baptiste Wicht
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#ifndef DBN_CONV_RBM_HPP
#define DBN_CONV_RBM_HPP

#include <cstddef>
#include <ctime>
#include <random>

#include "etl/fast_vector.hpp"
#include "etl/fast_matrix.hpp"
#include "etl/convolution.hpp"

#include "rbm_base.hpp"           //The base class
#include "unit_type.hpp"          //unit_ype enum
#include "assert.hpp"             //Assertions
#include "stop_watch.hpp"         //Performance counter
#include "math.hpp"               //Logistic sigmoid
#include "io.hpp"                 //Binary load/store functions
#include "vector.hpp"             //For samples
#include "tmp.hpp"

namespace dll {

template<typename RBM>
struct generic_trainer;

/*!
 * \brief Convolutional Restricted Boltzmann Machine
 */
template<typename Layer>
class conv_rbm : public rbm_base<Layer> {
public:
    typedef double weight;
    typedef double value_t;

    template<typename RBM>
    using trainer_t = typename Layer::template trainer_t<RBM>;

    static constexpr const bool Momentum = Layer::Momentum;
    static constexpr const std::size_t BatchSize = Layer::BatchSize;
    static constexpr const Type VisibleUnit = Layer::VisibleUnit;
    static constexpr const Type HiddenUnit = Layer::HiddenUnit;

    static constexpr const std::size_t NV = Layer::NV;
    static constexpr const std::size_t NH = Layer::NH;
    static constexpr const std::size_t K = Layer::K;

    static constexpr const std::size_t NW = NV - NH + 1; //By definition

    static constexpr const std::size_t num_visible = NV * NV;
    static constexpr const std::size_t num_hidden = NH * NH;

    static_assert(VisibleUnit == Type::SIGMOID, "Only binary visible units are supported");
    static_assert(HiddenUnit == Type::SIGMOID, "Only binary hidden units are supported");

    etl::fast_vector<etl::fast_matrix<weight, NW, NW>, K> w;     //shared weights
    etl::fast_vector<weight, K> b;                                //hidden biases bk
    weight c;                                                     //visible single bias c

    etl::fast_matrix<weight, NV, NV> v1;                         //visible units

    etl::fast_vector<etl::fast_matrix<weight, NH, NH>, K> h1_a;  //Activation probabilities of reconstructed hidden units
    etl::fast_vector<etl::fast_matrix<weight, NH, NH>, K> h1_s;  //Sampled values of reconstructed hidden units

    etl::fast_matrix<weight, NV, NV> v2_a;                       //Activation probabilities of reconstructed visible units
    etl::fast_matrix<weight, NV, NV> v2_s;                       //Sampled values of reconstructed visible units

    etl::fast_vector<etl::fast_matrix<weight, NH, NH>, K> h2_a;  //Activation probabilities of reconstructed hidden units
    etl::fast_vector<etl::fast_matrix<weight, NH, NH>, K> h2_s;  //Sampled values of reconstructed hidden units

    //Convolution data

    etl::fast_vector<etl::fast_matrix<weight, NH, NH>, K> v_cv_1;   //Temporary convolution
    etl::fast_vector<etl::fast_matrix<weight, NH, NH>, K> v_cv_2;   //Temporary convolution

    etl::fast_vector<etl::fast_matrix<weight, NV, NV>, K+1> h_cv_1;   //Temporary convolution
    etl::fast_vector<etl::fast_matrix<weight, NV, NV>, K+1> h_cv_2;   //Temporary convolution

public:
    //No copying
    conv_rbm(const conv_rbm& rbm) = delete;
    conv_rbm& operator=(const conv_rbm& rbm) = delete;

    //No moving
    conv_rbm(conv_rbm&& rbm) = delete;
    conv_rbm& operator=(conv_rbm&& rbm) = delete;

    conv_rbm(){}

    void store(std::ostream& os) const {
        for(std::size_t k = 0; k < K; ++k){
            binary_write_all(os, w(k));
        }
        binary_write_all(os, b);
        binary_write(os, c);
    }

    void load(std::istream& is){
        for(std::size_t k = 0; k < K; ++k){
            binary_load_all(is, w(k));
        }
        binary_load_all(is, b);
        binary_load(is, c);
    }

    template<typename H, typename V>
    void activate_hidden(H& h_a, H& h_s, const V& v_a, const V& v_s){
        activate_hidden(h_a, h_s, v_a, v_s, v_cv_1);
    }

    template<typename H, typename V, typename CV>
    void activate_hidden(H& h_a, H& h_s, const V& v_a, const V& v_s, CV& v_cv) const {
        static std::default_random_engine rand_engine(std::time(nullptr));
        static std::uniform_real_distribution<weight> normal_distribution(0.0, 1.0);
        static auto normal_generator = std::bind(normal_distribution, rand_engine);

        for(size_t k = 0; k < K; ++k){
            h_a(k) = 0.0;
            h_s(k) = 0.0;

            etl::convolve_2d_valid(v_a, w(k), v_cv(k));

            for(size_t i = 0; i < NH; ++i){
                for(size_t j = 0; j < NH; ++j){
                    //Total input
                    auto x = v_cv(k)(i,j) + b(k);

                    if(HiddenUnit == Type::SIGMOID){
                        h_a(k)(i,j) = logistic_sigmoid(x);
                        h_s(k)(i,j) = h_a(k)(i,j) > normal_generator() ? 1.0 : 0.0;
                    } else {
                        dll_unreachable("Invalid path");
                    }

                    dll_assert(std::isfinite(x), "NaN verify");
                    dll_assert(std::isfinite(h_a(k)(i,j)), "NaN verify");
                    dll_assert(std::isfinite(h_s(k)(i,j)), "NaN verify");
                }
            }
        }
    }

    template<typename H, typename V>
    void activate_visible(const H& h_a, const H& h_s, V& v_a, V& v_s){
        activate_visible(h_a, h_s, v_a, v_s, h_cv_1);
    }

    template<typename H, typename V, typename CV>
    void activate_visible(const H& h_a, const H& h_s, V& v_a, V& v_s, CV& h_cv) const {
        static std::default_random_engine rand_engine(std::time(nullptr));
        static std::uniform_real_distribution<weight> normal_distribution(0.0, 1.0);
        static auto normal_generator = std::bind(normal_distribution, rand_engine);

        v_a = 0.0;
        v_s = 0.0;

        for(std::size_t k = 0; k < K; ++k){
            etl::convolve_2d_full(h_s(k), w(k), h_cv(k));
            h_cv(K) += h_cv(k);
        }

        for(size_t i = 0; i < NV; ++i){
            for(size_t j = 0; j < NV; ++j){
                //Total input
                auto x = h_cv(K)(i,j) + c;

                if(HiddenUnit == Type::SIGMOID){
                    v_a(i,j) = logistic_sigmoid(x);
                    v_s(i,j) = v_a(i,j) > normal_generator() ? 1.0 : 0.0;
                } else {
                    dll_unreachable("Invalid path");
                }

                dll_assert(std::isfinite(x), "NaN verify");
                dll_assert(std::isfinite(v_a(i,j)), "NaN verify");
                dll_assert(std::isfinite(v_s(i,j)), "NaN verify");
            }
        }
    }

    void train(const std::vector<vector<weight>>& training_data, std::size_t max_epochs){
        typedef typename std::remove_reference<decltype(*this)>::type this_type;

        dll::generic_trainer<this_type> trainer;
        trainer.train(*this, training_data, max_epochs);
    }

    weight free_energy() const {
        weight energy = 0.0;

        //TODO Compute the exact free energy

        return energy;
    }

    //Utility functions

    void reconstruct(const vector<weight>& items){
        dll_assert(items.size() == num_visible, "The size of the training sample must match visible units");

        stop_watch<> watch;

        //Set the state of the visible units
        v1 = items;

        activate_hidden(h1_a, h1_s, v1, v1);
        activate_visible(h1_a, h1_s, v2_a, v2_s);
        activate_hidden(h2_a, h2_s, v2_a, v2_s);

        std::cout << "Reconstruction took " << watch.elapsed() << "ms" << std::endl;
    }

    void display_visible_units(size_t matrix) const {
        for(size_t i = 0; i < matrix; ++i){
            for(size_t j = 0; j < matrix; ++j){
                std::cout << v2_s(i * matrix + j) << " ";
            }
            std::cout << std::endl;
        }
    }
};

} //end of dbn namespace

#endif<commit_msg>Fix display methods<commit_after>//=======================================================================
// Copyright (c) 2014 Baptiste Wicht
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#ifndef DBN_CONV_RBM_HPP
#define DBN_CONV_RBM_HPP

#include <cstddef>
#include <ctime>
#include <random>

#include "etl/fast_vector.hpp"
#include "etl/fast_matrix.hpp"
#include "etl/convolution.hpp"

#include "rbm_base.hpp"           //The base class
#include "unit_type.hpp"          //unit_ype enum
#include "assert.hpp"             //Assertions
#include "stop_watch.hpp"         //Performance counter
#include "math.hpp"               //Logistic sigmoid
#include "io.hpp"                 //Binary load/store functions
#include "vector.hpp"             //For samples
#include "tmp.hpp"

namespace dll {

template<typename RBM>
struct generic_trainer;

/*!
 * \brief Convolutional Restricted Boltzmann Machine
 */
template<typename Layer>
class conv_rbm : public rbm_base<Layer> {
public:
    typedef double weight;
    typedef double value_t;

    template<typename RBM>
    using trainer_t = typename Layer::template trainer_t<RBM>;

    static constexpr const bool Momentum = Layer::Momentum;
    static constexpr const std::size_t BatchSize = Layer::BatchSize;
    static constexpr const Type VisibleUnit = Layer::VisibleUnit;
    static constexpr const Type HiddenUnit = Layer::HiddenUnit;

    static constexpr const std::size_t NV = Layer::NV;
    static constexpr const std::size_t NH = Layer::NH;
    static constexpr const std::size_t K = Layer::K;

    static constexpr const std::size_t NW = NV - NH + 1; //By definition

    static constexpr const std::size_t num_visible = NV * NV;
    static constexpr const std::size_t num_hidden = NH * NH;

    static_assert(VisibleUnit == Type::SIGMOID, "Only binary visible units are supported");
    static_assert(HiddenUnit == Type::SIGMOID, "Only binary hidden units are supported");

    etl::fast_vector<etl::fast_matrix<weight, NW, NW>, K> w;     //shared weights
    etl::fast_vector<weight, K> b;                                //hidden biases bk
    weight c;                                                     //visible single bias c

    etl::fast_matrix<weight, NV, NV> v1;                         //visible units

    etl::fast_vector<etl::fast_matrix<weight, NH, NH>, K> h1_a;  //Activation probabilities of reconstructed hidden units
    etl::fast_vector<etl::fast_matrix<weight, NH, NH>, K> h1_s;  //Sampled values of reconstructed hidden units

    etl::fast_matrix<weight, NV, NV> v2_a;                       //Activation probabilities of reconstructed visible units
    etl::fast_matrix<weight, NV, NV> v2_s;                       //Sampled values of reconstructed visible units

    etl::fast_vector<etl::fast_matrix<weight, NH, NH>, K> h2_a;  //Activation probabilities of reconstructed hidden units
    etl::fast_vector<etl::fast_matrix<weight, NH, NH>, K> h2_s;  //Sampled values of reconstructed hidden units

    //Convolution data

    etl::fast_vector<etl::fast_matrix<weight, NH, NH>, K> v_cv_1;   //Temporary convolution
    etl::fast_vector<etl::fast_matrix<weight, NH, NH>, K> v_cv_2;   //Temporary convolution

    etl::fast_vector<etl::fast_matrix<weight, NV, NV>, K+1> h_cv_1;   //Temporary convolution
    etl::fast_vector<etl::fast_matrix<weight, NV, NV>, K+1> h_cv_2;   //Temporary convolution

public:
    //No copying
    conv_rbm(const conv_rbm& rbm) = delete;
    conv_rbm& operator=(const conv_rbm& rbm) = delete;

    //No moving
    conv_rbm(conv_rbm&& rbm) = delete;
    conv_rbm& operator=(conv_rbm&& rbm) = delete;

    conv_rbm(){}

    void store(std::ostream& os) const {
        for(std::size_t k = 0; k < K; ++k){
            binary_write_all(os, w(k));
        }
        binary_write_all(os, b);
        binary_write(os, c);
    }

    void load(std::istream& is){
        for(std::size_t k = 0; k < K; ++k){
            binary_load_all(is, w(k));
        }
        binary_load_all(is, b);
        binary_load(is, c);
    }

    template<typename H, typename V>
    void activate_hidden(H& h_a, H& h_s, const V& v_a, const V& v_s){
        activate_hidden(h_a, h_s, v_a, v_s, v_cv_1);
    }

    template<typename H, typename V, typename CV>
    void activate_hidden(H& h_a, H& h_s, const V& v_a, const V& v_s, CV& v_cv) const {
        static std::default_random_engine rand_engine(std::time(nullptr));
        static std::uniform_real_distribution<weight> normal_distribution(0.0, 1.0);
        static auto normal_generator = std::bind(normal_distribution, rand_engine);

        for(size_t k = 0; k < K; ++k){
            h_a(k) = 0.0;
            h_s(k) = 0.0;

            etl::convolve_2d_valid(v_a, w(k), v_cv(k));

            for(size_t i = 0; i < NH; ++i){
                for(size_t j = 0; j < NH; ++j){
                    //Total input
                    auto x = v_cv(k)(i,j) + b(k);

                    if(HiddenUnit == Type::SIGMOID){
                        h_a(k)(i,j) = logistic_sigmoid(x);
                        h_s(k)(i,j) = h_a(k)(i,j) > normal_generator() ? 1.0 : 0.0;
                    } else {
                        dll_unreachable("Invalid path");
                    }

                    dll_assert(std::isfinite(x), "NaN verify");
                    dll_assert(std::isfinite(h_a(k)(i,j)), "NaN verify");
                    dll_assert(std::isfinite(h_s(k)(i,j)), "NaN verify");
                }
            }
        }
    }

    template<typename H, typename V>
    void activate_visible(const H& h_a, const H& h_s, V& v_a, V& v_s){
        activate_visible(h_a, h_s, v_a, v_s, h_cv_1);
    }

    template<typename H, typename V, typename CV>
    void activate_visible(const H& h_a, const H& h_s, V& v_a, V& v_s, CV& h_cv) const {
        static std::default_random_engine rand_engine(std::time(nullptr));
        static std::uniform_real_distribution<weight> normal_distribution(0.0, 1.0);
        static auto normal_generator = std::bind(normal_distribution, rand_engine);

        v_a = 0.0;
        v_s = 0.0;

        for(std::size_t k = 0; k < K; ++k){
            etl::convolve_2d_full(h_s(k), w(k), h_cv(k));
            h_cv(K) += h_cv(k);
        }

        for(size_t i = 0; i < NV; ++i){
            for(size_t j = 0; j < NV; ++j){
                //Total input
                auto x = h_cv(K)(i,j) + c;

                if(HiddenUnit == Type::SIGMOID){
                    v_a(i,j) = logistic_sigmoid(x);
                    v_s(i,j) = v_a(i,j) > normal_generator() ? 1.0 : 0.0;
                } else {
                    dll_unreachable("Invalid path");
                }

                dll_assert(std::isfinite(x), "NaN verify");
                dll_assert(std::isfinite(v_a(i,j)), "NaN verify");
                dll_assert(std::isfinite(v_s(i,j)), "NaN verify");
            }
        }
    }

    void train(const std::vector<vector<weight>>& training_data, std::size_t max_epochs){
        typedef typename std::remove_reference<decltype(*this)>::type this_type;

        dll::generic_trainer<this_type> trainer;
        trainer.train(*this, training_data, max_epochs);
    }

    weight free_energy() const {
        weight energy = 0.0;

        //TODO Compute the exact free energy

        return energy;
    }

    //Utility functions

    void reconstruct(const vector<weight>& items){
        dll_assert(items.size() == num_visible, "The size of the training sample must match visible units");

        stop_watch<> watch;

        //Set the state of the visible units
        v1 = items;

        activate_hidden(h1_a, h1_s, v1, v1);
        activate_visible(h1_a, h1_s, v2_a, v2_s);
        activate_hidden(h2_a, h2_s, v2_a, v2_s);

        std::cout << "Reconstruction took " << watch.elapsed() << "ms" << std::endl;
    }

    void display_visible_units() const {
        for(size_t i = 0; i < NV; ++i){
            for(size_t j = 0; j < NV; ++j){
                std::cout << v2_s(i, j) << " ";
            }
            std::cout << std::endl;
        }
    }

    void display_hidden_units() const {
        for(size_t k = 0; k < K; ++k){
            for(size_t i = 0; i < NV; ++i){
                for(size_t j = 0; j < NV; ++j){
                    std::cout << h2_s(i, j) << " ";
                }
                std::cout << std::endl;
            }
            std::cout << std::endl << std::endl;
        }
    }
};

} //end of dbn namespace

#endif<|endoftext|>