<commit_before>// g++ j.cc -o j -L/usr/local/lib -lroaring
#include <iostream>
#include <vector>
#include <string>
#include <array>
#include <map>
#include "roaring.hh"

enum FieldType {
  TIMESTAMP,
  DIMENSION,
  BOOL,
  METRIC_INT,
  METRIC_FLOAT
};

class FieldBase {};

template <FieldType, typename T>
class Field : FieldBase {
 private:
  std::string name;
  std::vector<T> vals;
  std::vector< std::vector<T> > mvals;
  std::map<T, Roaring *> dict_;
  Roaring *roar_;
  int count_;
  FieldType type_;
 public:
  Field(std::string name);
  void insert(T &val);
  std::map<std::string, Roaring *> &dict();
  Roaring *roar() {
    return roar_;
  }
};

template <FieldType type, typename T>
Field<type, T>::Field(std::string name):
    type_(type), name(name) {
  switch (type) {
    case METRIC_INT:
    case METRIC_FLOAT: {
    } break;
    default:
      throw std::runtime_error("invalid field type for Field<>");
  }
}

template <>
Field<DIMENSION, std::string>::Field(std::string name):
    type_(DIMENSION), name(name) {
  // switch (type) {
  //   case DIMENSION: {
  //   } break;
  //   default:
  //     throw std::runtime_error("invalid field type for string");
  // }
}

template <>
Field<TIMESTAMP, int64_t>::Field(std::string name):
    type_(TIMESTAMP), name(name) {
  // switch (type) {
  //   case TIMESTAMP: {
  //   } break;
  //   default:
  //     throw std::runtime_error("invalid field type for int64");
  // }
}

template <>
Field<BOOL, bool>::Field(std::string name):
    type_(BOOL), name(name) {
  roar_ = new Roaring();
  // switch (type) {
  //   case BOOL: {
  //     roar_ = new Roaring();
  //   } break;
  //   default:
  //     throw std::runtime_error("invalid field type for bool");
  // }
}

template <FieldType type, typename T>
void Field<type, T>::insert(T &val) {
  switch (type_) {
    case TIMESTAMP:
    case METRIC_INT:
    case METRIC_FLOAT: {
      vals.push_back(val);
    } break;
    case DIMENSION: {
      Roaring *roar;
      if (dict_.count(val) > 0) {
        roar = dict_[val];
      } else {
        roar = new Roaring();
        dict_[val] = roar;
      }
      roar->add(count_);
    } break;
    default:
      throw std::runtime_error("can not insert val to field with unknown type");
  }

  count_++;
}

template<>
std::map<std::string, Roaring *> &Field<DIMENSION, std::string>::dict() {
  return dict_;
}

class DimensionField {
 public:
  Field<DIMENSION, std::string> *strdim;
  Field<BOOL, bool> *booldim;
  bool isBoolDim;
  DimensionField(Field<DIMENSION, std::string> *strdim_): strdim(strdim_), isBoolDim(false) {}
  DimensionField(Field<BOOL, bool> *booldim_): booldim(booldim_), isBoolDim(true) {}
};

class Query {
 private:
 public:
  Query();
  ~Query();
};

struct GroupByResult {
  std::vector<std::string> key;
  Roaring roaring;
};

static void genGroups(std::vector<GroupByResult> &groups, std::vector<std::string> &groupByKeys, std::map<std::string, FieldBase*> &fields, std::vector<std::string> currKey, int index) {
  bool genGroup = index == (groupByKeys.size() - 1);
  std::string key = groupByKeys[index];
  FieldBase *fieldRef = fields[key];
  Field<DIMENSION, std::string> *field = (Field<DIMENSION, std::string> *)fieldRef;
  std::map<std::string, Roaring *> &dict = field->dict();
  for (std::map<std::string, Roaring *>::iterator it = dict.begin(); it != dict.end(); it++) {
    std::vector<std::string> groupKey = currKey;
    groupKey.push_back(it->first);

    if (genGroup) {
      GroupByResult gres;
      gres.key = groupKey;
      groups.push_back(gres);
    } else {
      genGroups(groups, groupByKeys, fields, groupKey, index + 1);
    }
  }
}

std::vector<GroupByResult> genGroupByResult(std::vector<std::string> &groupByKeys, std::map<std::string, FieldBase*> &fields) {
  std::vector<GroupByResult> groups;
  genGroups(groups, groupByKeys, fields, std::vector<std::string>(), 0);
  return groups;
}

int main(int argc, char *argv[]) {
  Field<TIMESTAMP, int64_t> *timestamp = new Field<TIMESTAMP, int64_t>("timestamp");
  Field<DIMENSION, std::string> *publisher = new Field<DIMENSION, std::string>("publisher");
  Field<DIMENSION, std::string> *advertiser = new Field<DIMENSION, std::string>("advertiser");
  Field<DIMENSION, std::string> *gender = new Field<DIMENSION, std::string>("gender");
  Field<DIMENSION, std::string> *country = new Field<DIMENSION, std::string>("country");
  Field<BOOL, bool> *click = new Field<BOOL, bool>("click");
  Field<METRIC_FLOAT, float> *price = new Field<METRIC_FLOAT, float>("price");
  std::map<std::string, FieldBase*> fields;

  fields["timestamp"] = (FieldBase*)timestamp;
  fields["publisher"] = (FieldBase*)publisher;
  fields["advertiser"] = (FieldBase*)advertiser;
  fields["gender"] = (FieldBase*)gender;
  fields["country"] = (FieldBase*)country;
  fields["click"] = (FieldBase*)click;
  fields["price"] = (FieldBase*)price;

  // select count(*) from logs where click = 1 group by country,gender
  {
    // generate new bitmap of all elements. so we need segment wide count variable
    int count = 3;
    Roaring roar(roaring_bitmap_from_range(0, count, 1));

    //////////////////////////////////////////////////////////////////////
    /////////// FILTERING STATE //////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////

    // do bitwise AND with click = 1
    // roar.printf();
    roar &= click->roar();
    // roar.printf();

    //////////////////////////////////////////////////////////////////////
    /////////// GROUPING STATE ///////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////

    // generate groups for every group by term
    std::vector< GroupByResult > groups;
    std::vector<std::string> groupByKeys;
    groupByKeys.push_back("advertiser");
    groupByKeys.push_back("gender");
    groupByKeys.push_back("country");

    groups = genGroupByResult(groupByKeys, fields);

    for (std::vector< GroupByResult >::iterator it = groups.begin(); it != groups.end(); it++) {
      const GroupByResult &gres = *it;
      std::vector<std::string> key = gres.key;
      for (std::vector<std::string>::iterator it2 = key.begin(); it2 != key.end(); it2++) {
        std::cout << *it2;
        if (it2 != key.end()) {
          std::cout << ", ";
        }
      }
      std::cout << std::endl;
    }
  }

  return 0;
}

<commit_msg>move fields to outside of main<commit_after>// g++ j.cc -o j -L/usr/local/lib -lroaring
#include <iostream>
#include <vector>
#include <string>
#include <array>
#include <map>
#include "roaring.hh"

enum FieldType {
  TIMESTAMP,
  DIMENSION,
  BOOL,
  METRIC_INT,
  METRIC_FLOAT
};

class FieldBase {};

template <FieldType, typename T>
class Field : FieldBase {
 private:
  std::string name;
  std::vector<T> vals;
  std::vector< std::vector<T> > mvals;
  std::map<T, Roaring *> dict_;
  Roaring *roar_;
  int count_;
  FieldType type_;
 public:
  Field(std::string name);
  void insert(T &val);
  std::map<std::string, Roaring *> &dict();
  Roaring *roar() {
    return roar_;
  }
};

template <FieldType type, typename T>
Field<type, T>::Field(std::string name):
    type_(type), name(name) {
  switch (type) {
    case METRIC_INT:
    case METRIC_FLOAT: {
    } break;
    default:
      throw std::runtime_error("invalid field type for Field<>");
  }
}

template <>
Field<DIMENSION, std::string>::Field(std::string name):
    type_(DIMENSION), name(name) {
  // switch (type) {
  //   case DIMENSION: {
  //   } break;
  //   default:
  //     throw std::runtime_error("invalid field type for string");
  // }
}

template <>
Field<TIMESTAMP, int64_t>::Field(std::string name):
    type_(TIMESTAMP), name(name) {
  // switch (type) {
  //   case TIMESTAMP: {
  //   } break;
  //   default:
  //     throw std::runtime_error("invalid field type for int64");
  // }
}

template <>
Field<BOOL, bool>::Field(std::string name):
    type_(BOOL), name(name) {
  roar_ = new Roaring();
  // switch (type) {
  //   case BOOL: {
  //     roar_ = new Roaring();
  //   } break;
  //   default:
  //     throw std::runtime_error("invalid field type for bool");
  // }
}

template <FieldType type, typename T>
void Field<type, T>::insert(T &val) {
  switch (type_) {
    case TIMESTAMP:
    case METRIC_INT:
    case METRIC_FLOAT: {
      vals.push_back(val);
    } break;
    case DIMENSION: {
      Roaring *roar;
      if (dict_.count(val) > 0) {
        roar = dict_[val];
      } else {
        roar = new Roaring();
        dict_[val] = roar;
      }
      roar->add(count_);
    } break;
    default:
      throw std::runtime_error("can not insert val to field with unknown type");
  }

  count_++;
}

template<>
std::map<std::string, Roaring *> &Field<DIMENSION, std::string>::dict() {
  return dict_;
}

class DimensionField {
 public:
  Field<DIMENSION, std::string> *strdim;
  Field<BOOL, bool> *booldim;
  bool isBoolDim;
  DimensionField(Field<DIMENSION, std::string> *strdim_): strdim(strdim_), isBoolDim(false) {}
  DimensionField(Field<BOOL, bool> *booldim_): booldim(booldim_), isBoolDim(true) {}
};

class Query {
 private:
 public:
  Query();
  ~Query();
};

struct GroupByResult {
  std::vector<std::string> key;
  Roaring roaring;
};

static void genGroups(std::vector<GroupByResult> &groups, std::vector<std::string> &groupByKeys, std::map<std::string, FieldBase*> &fields, std::vector<std::string> currKey, int index) {
  bool genGroup = index == (groupByKeys.size() - 1);
  std::string key = groupByKeys[index];
  FieldBase *fieldRef = fields[key];
  Field<DIMENSION, std::string> *field = (Field<DIMENSION, std::string> *)fieldRef;
  std::map<std::string, Roaring *> &dict = field->dict();
  for (std::map<std::string, Roaring *>::iterator it = dict.begin(); it != dict.end(); it++) {
    std::vector<std::string> groupKey = currKey;
    groupKey.push_back(it->first);

    if (genGroup) {
      GroupByResult gres;
      gres.key = groupKey;
      groups.push_back(gres);
    } else {
      genGroups(groups, groupByKeys, fields, groupKey, index + 1);
    }
  }
}

std::vector<GroupByResult> genGroupByResult(std::vector<std::string> &groupByKeys, std::map<std::string, FieldBase*> &fields) {
  std::vector<GroupByResult> groups;
  genGroups(groups, groupByKeys, fields, std::vector<std::string>(), 0);
  return groups;
}

/// global fields
Field<TIMESTAMP, int64_t> *timestamp;
Field<DIMENSION, std::string> *publisher;
Field<DIMENSION, std::string> *advertiser;
Field<DIMENSION, std::string> *gender;
Field<DIMENSION, std::string> *country;
Field<BOOL, bool> *click;
Field<METRIC_FLOAT, float> *price;

int main(int argc, char *argv[]) {
  timestamp = new Field<TIMESTAMP, int64_t>("timestamp");
  publisher = new Field<DIMENSION, std::string>("publisher");
  advertiser = new Field<DIMENSION, std::string>("advertiser");
  gender = new Field<DIMENSION, std::string>("gender");
  country = new Field<DIMENSION, std::string>("country");
  click = new Field<BOOL, bool>("click");
  price = new Field<METRIC_FLOAT, float>("price");

  std::map<std::string, FieldBase*> fields;

  fields["timestamp"] = (FieldBase*)timestamp;
  fields["publisher"] = (FieldBase*)publisher;
  fields["advertiser"] = (FieldBase*)advertiser;
  fields["gender"] = (FieldBase*)gender;
  fields["country"] = (FieldBase*)country;
  fields["click"] = (FieldBase*)click;
  fields["price"] = (FieldBase*)price;

  // select count(*) from logs where click = 1 group by country,gender
  {
    // generate new bitmap of all elements. so we need segment wide count variable
    int count = 3;
    Roaring roar(roaring_bitmap_from_range(0, count, 1));

    //////////////////////////////////////////////////////////////////////
    /////////// FILTERING STATE //////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////

    // do bitwise AND with click = 1
    // roar.printf();
    roar &= click->roar();
    // roar.printf();

    //////////////////////////////////////////////////////////////////////
    /////////// GROUPING STATE ///////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////

    // generate groups for every group by term
    std::vector< GroupByResult > groups;
    std::vector<std::string> groupByKeys;
    groupByKeys.push_back("advertiser");
    groupByKeys.push_back("gender");
    groupByKeys.push_back("country");

    groups = genGroupByResult(groupByKeys, fields);

    for (std::vector< GroupByResult >::iterator it = groups.begin(); it != groups.end(); it++) {
      const GroupByResult &gres = *it;
      std::vector<std::string> key = gres.key;
      for (std::vector<std::string>::iterator it2 = key.begin(); it2 != key.end(); it2++) {
        std::cout << *it2;
        if (it2 != key.end()) {
          std::cout << ", ";
        }
      }
      std::cout << std::endl;
    }
  }

  return 0;
}

<|endoftext|>


<commit_before>#include <cstdio>
#include <vector>
#include <stack>
#include <algorithm>
#include <memory>

namespace Frogs
{
    enum class Frog  { None, Brown, Green };
    enum class Step  { None, JumpLeft, LeapLeft, JumpRight, LeapRight };

    class State
    {
        std::vector<Frog> lilies;
        int count, blankPos;
        Step movement;

    public:
        State(): count(0), blankPos(-1), movement(Step::None)
        {
        }

        State(int count): lilies(2 * count + 1), count(count), blankPos(count), movement(Step::None)
        {
            for (int i = 0; i < count; ++i)
            {
                lilies[i] = Frog::Brown;
                lilies[2 * count - i] = Frog::Green;
            }

            lilies[blankPos] = Frog::None;
        }

        bool operator==(const State& other) const
        {
            return count == other.count && lilies == other.lilies && blankPos == other.blankPos && movement == other.movement;
        }

        Step GetMovement()      const  { return movement; }

        bool IsStuckJumpLeft()  const  { return blankPos >= (int)lilies.size() - 1 || lilies[blankPos + 1] != Frog::Green; }
        bool IsStuckLeapLeft()  const  { return blankPos >= (int)lilies.size() - 2 || lilies[blankPos + 2] != Frog::Green; }
        bool IsStuckJumpRight() const  { return blankPos < 1                       || lilies[blankPos - 1] != Frog::Brown; }
        bool IsStuckLeapRight() const  { return blankPos < 2                       || lilies[blankPos - 2] != Frog::Brown; }

        bool WasTargetReached() const
        {
            for (int i = 0; i < count; ++i)
                if (lilies[i] != Frog::Green || lilies[2 * count - i] != Frog::Brown)
                    return false;

            return lilies[count] == Frog::None;
        };

        void Print() const
        {
            for (const auto& frog: lilies)
                switch (frog)
                {
                    case Frog::Brown:
                        printf(">");
                        break;

                    case Frog::Green:
                        printf("<");
                        break;

                    case Frog::None:
                        printf(" ");
                        break;
                }

            printf("\n");
        }

        std::shared_ptr<State> Move(Step movement) const
        {
            bool canMove = false;
            auto moved = std::make_shared<State>(*this);
            moved->movement = movement;

            switch (movement)
            {
                case Step::JumpLeft:
                    if (!IsStuckJumpLeft())
                    {
                        moved->lilies[moved->blankPos] = Frog::Green;
                        moved->blankPos++;
                        canMove = true;
                    }
                    break;

                case Step::LeapLeft:
                    if (!IsStuckLeapLeft())
                    {
                        moved->lilies[moved->blankPos] = Frog::Green;
                        moved->blankPos += 2;
                        canMove = true;
                    }
                    break;

                case Step::JumpRight:
                    if (!IsStuckJumpRight())
                    {
                        moved->lilies[moved->blankPos] = Frog::Brown;
                        moved->blankPos--;
                        canMove = true;
                    }
                    break;

                case Step::LeapRight:
                    if (!IsStuckLeapRight())
                    {
                        moved->lilies[moved->blankPos] = Frog::Brown;
                        moved->blankPos -= 2;
                        canMove = true;
                    }
                    break;

                default:
                    break;
            }

            if (canMove)
                moved->lilies[moved->blankPos] = Frog::None;
            return canMove ? moved : nullptr;
        }

        std::shared_ptr<State> UndoStep(Step prevMovement) const
        {
            bool canUndoStep = false;
            auto undone = std::make_shared<State>(*this);

            switch (movement)
            {
                case Step::JumpLeft:
                    undone->lilies[blankPos] = Frog::Green;
                    undone->blankPos--;
                    canUndoStep = true;
                    break;

                case Step::LeapLeft:
                    undone->lilies[blankPos] = Frog::Green;
                    undone->blankPos -= 2;
                    canUndoStep = true;
                    break;

                case Step::JumpRight:
                    undone->lilies[blankPos] = Frog::Brown;
                    undone->blankPos++;
                    canUndoStep = true;
                    break;

                case Step::LeapRight:
                    undone->lilies[blankPos] = Frog::Brown;
                    undone->blankPos += 2;
                    canUndoStep = true;
                    break;

                default:
                    break;
            }

            if (canUndoStep)
            {
                undone->lilies[blankPos] = Frog::None;
                undone->movement = prevMovement;
            }
            return canUndoStep ? undone : nullptr;
        };
    };
}

int main()
{
    int count;
    scanf("%u", &count);

    if (count < 0)
        return 1;

    std::vector<std::shared_ptr<Frogs::State> > visited;
    std::stack<std::shared_ptr<Frogs::State> > trace;
    std::stack<Frogs::Step> movements;
    trace.push(std::make_shared<Frogs::State>(count));

    while (!trace.empty() && !trace.top()->WasTargetReached())
    {
        auto state = trace.top();
        trace.pop();

        auto move = [&](Frogs::Step movement)
        {
            auto newState = state->Move(movement);
            if (newState)
                trace.push(newState);
        };

        while (!movements.empty() && state && state->IsStuckJumpLeft() && state->IsStuckLeapLeft() && state->IsStuckJumpRight() && state->IsStuckLeapRight())
        {
            state = state->UndoStep(movements.top());
            movements.pop();
        }

        if (!state)
            continue;

        if (std::find(visited.crbegin(), visited.crend(), state) == visited.crend())
        {
            visited.push_back(state);
            movements.push(state->GetMovement());
            move(Frogs::Step::JumpLeft);
            move(Frogs::Step::LeapLeft);
            move(Frogs::Step::JumpRight);
            move(Frogs::Step::LeapRight);

            state->Print();
        }
    }

    if (!trace.empty())
        trace.top()->Print();

    return 0;
}
<commit_msg>reordered things around<commit_after>#include <cstdio>
#include <vector>
#include <stack>
#include <algorithm>
#include <memory>

namespace Frogs
{
    enum class Frog  { None, Brown, Green };
    enum class Step  { None, JumpLeft, LeapLeft, JumpRight, LeapRight };

    class State
    {
        std::vector<Frog> lilies;
        int count, blankPos;
        Step movement;

    public:
        State(): count(0), blankPos(-1), movement(Step::None)
        {
        }
        State(int count): lilies(2 * count + 1), count(count), blankPos(count), movement(Step::None)
        {
            for (int i = 0; i < count; ++i)
            {
                lilies[i] = Frog::Brown;
                lilies[2 * count - i] = Frog::Green;
            }

            lilies[blankPos] = Frog::None;
        }

        Step GetMovement()      const  { return movement; }

        bool operator==(const State& other) const
        {
            return count == other.count && lilies == other.lilies && blankPos == other.blankPos && movement == other.movement;
        }

        bool IsStuckJumpLeft()  const  { return blankPos >= (int)lilies.size() - 1 || lilies[blankPos + 1] != Frog::Green; }
        bool IsStuckLeapLeft()  const  { return blankPos >= (int)lilies.size() - 2 || lilies[blankPos + 2] != Frog::Green; }
        bool IsStuckJumpRight() const  { return blankPos < 1                       || lilies[blankPos - 1] != Frog::Brown; }
        bool IsStuckLeapRight() const  { return blankPos < 2                       || lilies[blankPos - 2] != Frog::Brown; }

        bool WasTargetReached() const
        {
            for (int i = 0; i < count; ++i)
                if (lilies[i] != Frog::Green || lilies[2 * count - i] != Frog::Brown)
                    return false;

            return lilies[count] == Frog::None;
        }

        std::shared_ptr<State> Move(Step movement) const
        {
            bool canMove = false;
            auto moved = std::make_shared<State>(*this);
            moved->movement = movement;

            switch (movement)
            {
                case Step::JumpLeft:
                    if (!IsStuckJumpLeft())
                    {
                        moved->lilies[moved->blankPos] = Frog::Green;
                        moved->blankPos++;
                        canMove = true;
                    }
                    break;

                case Step::LeapLeft:
                    if (!IsStuckLeapLeft())
                    {
                        moved->lilies[moved->blankPos] = Frog::Green;
                        moved->blankPos += 2;
                        canMove = true;
                    }
                    break;

                case Step::JumpRight:
                    if (!IsStuckJumpRight())
                    {
                        moved->lilies[moved->blankPos] = Frog::Brown;
                        moved->blankPos--;
                        canMove = true;
                    }
                    break;

                case Step::LeapRight:
                    if (!IsStuckLeapRight())
                    {
                        moved->lilies[moved->blankPos] = Frog::Brown;
                        moved->blankPos -= 2;
                        canMove = true;
                    }
                    break;

                default:
                    break;
            }

            if (canMove)
                moved->lilies[moved->blankPos] = Frog::None;
            return canMove ? moved : nullptr;
        }
        std::shared_ptr<State> UndoStep(Step prevMovement) const
        {
            bool canUndoStep = false;
            auto undone = std::make_shared<State>(*this);

            switch (movement)
            {
                case Step::JumpLeft:
                    undone->lilies[blankPos] = Frog::Green;
                    undone->blankPos--;
                    canUndoStep = true;
                    break;

                case Step::LeapLeft:
                    undone->lilies[blankPos] = Frog::Green;
                    undone->blankPos -= 2;
                    canUndoStep = true;
                    break;

                case Step::JumpRight:
                    undone->lilies[blankPos] = Frog::Brown;
                    undone->blankPos++;
                    canUndoStep = true;
                    break;

                case Step::LeapRight:
                    undone->lilies[blankPos] = Frog::Brown;
                    undone->blankPos += 2;
                    canUndoStep = true;
                    break;

                default:
                    break;
            }

            if (canUndoStep)
            {
                undone->lilies[blankPos] = Frog::None;
                undone->movement = prevMovement;
            }
            return canUndoStep ? undone : nullptr;
        }

        void Print() const
        {
            for (const auto& frog: lilies)
                switch (frog)
                {
                    case Frog::Brown:
                        printf(">");
                        break;

                    case Frog::Green:
                        printf("<");
                        break;

                    case Frog::None:
                        printf(" ");
                        break;
                }

            printf("\n");
        }
    };
}

int main()
{
    int count;
    scanf("%u", &count);

    if (count < 0)
        return 1;

    std::vector<std::shared_ptr<Frogs::State> > visited;
    std::stack<std::shared_ptr<Frogs::State> > trace;
    std::stack<Frogs::Step> movements;
    trace.push(std::make_shared<Frogs::State>(count));

    while (!trace.empty() && !trace.top()->WasTargetReached())
    {
        auto state = trace.top();
        trace.pop();

        auto move = [&](Frogs::Step movement)
        {
            auto newState = state->Move(movement);
            if (newState)
                trace.push(newState);
        };

        while (!movements.empty() && state && state->IsStuckJumpLeft() && state->IsStuckLeapLeft() && state->IsStuckJumpRight() && state->IsStuckLeapRight())
        {
            state = state->UndoStep(movements.top());
            movements.pop();
        }

        if (!state)
            continue;

        if (std::find(visited.crbegin(), visited.crend(), state) == visited.crend())
        {
            visited.push_back(state);
            movements.push(state->GetMovement());
            move(Frogs::Step::JumpLeft);
            move(Frogs::Step::LeapLeft);
            move(Frogs::Step::JumpRight);
            move(Frogs::Step::LeapRight);

            state->Print();
        }
    }

    if (!trace.empty())
        trace.top()->Print();

    return 0;
}
<|endoftext|>


<commit_before>/* -*-  mode: c++; c-default-style: "google"; indent-tabs-mode: nil -*- */

/*
  Evaluates the conductivity of surface flow.

  Authors: Ethan Coon (ecoon@lanl.gov)
*/

#include "independent_variable_field_evaluator.hh"
#include "overland_conductivity_evaluator.hh"
#include "manning_conductivity_model.hh"

namespace Amanzi {
namespace Flow {
namespace FlowRelations {

OverlandConductivityEvaluator::OverlandConductivityEvaluator(Teuchos::ParameterList& plist) :
    SecondaryVariableFieldEvaluator(plist) {

  depth_key_ = plist_.get<std::string>("height key", "ponded_depth");
  dependencies_.insert(depth_key_);

  slope_key_ = plist_.get<std::string>("slope key", "slope_magnitude");
  dependencies_.insert(slope_key_);

  coef_key_ = plist_.get<std::string>("coefficient key", "manning_coefficient");
  dependencies_.insert(coef_key_);

  dt_ = plist_.get<bool>("include dt factor", false);
  if (dt_) {
    factor_ = plist_.get<double>("dt factor");
  }
  dens_ = plist_.get<bool>("include density factor", true);

  if (dens_) {
    dens_key_ = plist_.get<std::string>("density key", "surface_molar_density_liquid");
    dependencies_.insert(dens_key_);
  }

  if (my_key_ == std::string("")) {
    my_key_ = plist_.get<std::string>("overland conductivity key",
            "overland_conductivity");
  }

  // create the model, hard-coded until we have a 2nd model
  ASSERT(plist_.isSublist("overland conductivity model"));
  Teuchos::ParameterList sublist = plist_.sublist("overland conductivity model");
  model_ = Teuchos::rcp(new ManningConductivityModel(sublist));
}

OverlandConductivityEvaluator::OverlandConductivityEvaluator(const OverlandConductivityEvaluator& other) :
    SecondaryVariableFieldEvaluator(other),
    depth_key_(other.depth_key_),
    slope_key_(other.slope_key_),
    coef_key_(other.coef_key_),
    dens_key_(other.dens_key_),
    dt_(other.dt_),
    factor_(other.factor_),
    dens_(other.dens_),
    model_(other.model_) {}

Teuchos::RCP<FieldEvaluator>
OverlandConductivityEvaluator::Clone() const {
  return Teuchos::rcp(new OverlandConductivityEvaluator(*this));
}

// Required methods from SecondaryVariableFieldEvaluator
void OverlandConductivityEvaluator::EvaluateField_(const Teuchos::Ptr<State>& S,
        const Teuchos::Ptr<CompositeVector>& result) {

  Teuchos::RCP<const CompositeVector> depth = S->GetFieldData(depth_key_);
  Teuchos::RCP<const CompositeVector> slope = S->GetFieldData(slope_key_);
  Teuchos::RCP<const CompositeVector> coef = S->GetFieldData(coef_key_);

  for (CompositeVector::name_iterator comp=result->begin();
       comp!=result->end(); ++comp) {
    const Epetra_MultiVector& depth_v = *depth->ViewComponent(*comp,false);
    const Epetra_MultiVector& slope_v = *slope->ViewComponent(*comp,false);
    const Epetra_MultiVector& coef_v = *coef->ViewComponent(*comp,false);
    Epetra_MultiVector& result_v = *result->ViewComponent(*comp,false);

    int ncomp = result->size(*comp, false);
    if (dt_) {
      for (int i=0; i!=ncomp; ++i) {
        result_v[0][i] = model_->Conductivity(factor_*depth_v[0][i], slope_v[0][i], coef_v[0][i]);
      }
    } else {
      //      double dt = *S->GetScalarData("dt");
      for (int i=0; i!=ncomp; ++i) {
        result_v[0][i] = model_->Conductivity(depth_v[0][i], slope_v[0][i], coef_v[0][i]);
      }
    }

    if (dens_) {
      const Epetra_MultiVector& dens_v = *S->GetFieldData(dens_key_)->ViewComponent(*comp,false);
      for (int i=0; i!=ncomp; ++i) {
        result_v[0][i] *= dens_v[0][i];
      }
    }
  }
}

void OverlandConductivityEvaluator::EvaluateFieldPartialDerivative_(
    const Teuchos::Ptr<State>& S,
    Key wrt_key, const Teuchos::Ptr<CompositeVector>& result) {

  Teuchos::RCP<const CompositeVector> depth = S->GetFieldData(depth_key_);
  Teuchos::RCP<const CompositeVector> slope = S->GetFieldData(slope_key_);
  Teuchos::RCP<const CompositeVector> coef = S->GetFieldData(coef_key_);

  if (wrt_key == depth_key_) {
    for (CompositeVector::name_iterator comp=result->begin();
         comp!=result->end(); ++comp) {
      const Epetra_MultiVector& depth_v = *depth->ViewComponent(*comp,false);
      const Epetra_MultiVector& slope_v = *slope->ViewComponent(*comp,false);
      const Epetra_MultiVector& coef_v = *coef->ViewComponent(*comp,false);
      Epetra_MultiVector& result_v = *result->ViewComponent(*comp,false);

      int ncomp = result->size(*comp, false);
      if (dt_) {
        //        double dt = *S->GetScalarData("dt");
        for (int i=0; i!=ncomp; ++i) {
          result_v[0][i] = model_->DConductivityDDepth(factor_*depth_v[0][i], slope_v[0][i], coef_v[0][i]) * factor_;
        }
      } else {
        for (int i=0; i!=ncomp; ++i) {
          result_v[0][i] = model_->DConductivityDDepth(depth_v[0][i], slope_v[0][i], coef_v[0][i]);
        }
      }

      if (dens_) {
        const Epetra_MultiVector& dens_v = *S->GetFieldData(dens_key_)->ViewComponent(*comp,false);
        for (int i=0; i!=ncomp; ++i) {
          result_v[0][i] *= dens_v[0][i];
        }
      }
    }

  } else if (wrt_key == dens_key_) {
    ASSERT(dens_);
    for (CompositeVector::name_iterator comp=result->begin();
         comp!=result->end(); ++comp) {
      const Epetra_MultiVector& depth_v = *depth->ViewComponent(*comp,false);
      const Epetra_MultiVector& slope_v = *slope->ViewComponent(*comp,false);
      const Epetra_MultiVector& coef_v = *coef->ViewComponent(*comp,false);
      Epetra_MultiVector& result_v = *result->ViewComponent(*comp,false);

      int ncomp = result->size(*comp, false);
      if (dt_) {
        //        double dt = *S->GetScalarData("dt");
        for (int i=0; i!=ncomp; ++i) {
          result_v[0][i] = model_->Conductivity(factor_*depth_v[0][i], slope_v[0][i], coef_v[0][i]);
        }
      } else {
        for (int i=0; i!=ncomp; ++i) {
          result_v[0][i] = model_->Conductivity(depth_v[0][i], slope_v[0][i], coef_v[0][i]);
        }
      }
    }

  } else {
    ASSERT(0);
  }
}

} //namespace
} //namespace
} //namespace

<commit_msg>adds option for which overland conductivity model to use, as we will be adding a no-denominator version<commit_after>/* -*-  mode: c++; c-default-style: "google"; indent-tabs-mode: nil -*- */

/*
  Evaluates the conductivity of surface flow.

  Authors: Ethan Coon (ecoon@lanl.gov)
*/

#include "independent_variable_field_evaluator.hh"
#include "overland_conductivity_evaluator.hh"
#include "manning_conductivity_model.hh"
// #include "no_denominator_conductivity_model.hh"

namespace Amanzi {
namespace Flow {
namespace FlowRelations {

OverlandConductivityEvaluator::OverlandConductivityEvaluator(Teuchos::ParameterList& plist) :
    SecondaryVariableFieldEvaluator(plist) {

  depth_key_ = plist_.get<std::string>("height key", "ponded_depth");
  dependencies_.insert(depth_key_);

  slope_key_ = plist_.get<std::string>("slope key", "slope_magnitude");
  dependencies_.insert(slope_key_);

  coef_key_ = plist_.get<std::string>("coefficient key", "manning_coefficient");
  dependencies_.insert(coef_key_);

  dt_ = plist_.get<bool>("include dt factor", false);
  if (dt_) {
    factor_ = plist_.get<double>("dt factor");
  }
  dens_ = plist_.get<bool>("include density factor", true);

  if (dens_) {
    dens_key_ = plist_.get<std::string>("density key", "surface_molar_density_liquid");
    dependencies_.insert(dens_key_);
  }

  if (my_key_ == std::string("")) {
    my_key_ = plist_.get<std::string>("overland conductivity key",
            "overland_conductivity");
  }

  // create the model, hard-coded until we have a 2nd model
  ASSERT(plist_.isSublist("overland conductivity model"));
  Teuchos::ParameterList sublist = plist_.sublist("overland conductivity model");
  std::string model_type = sublist.get<std::string>("overland conductivity type", "manning");
  if (model_type == "manning") {
    model_ = Teuchos::rcp(new ManningConductivityModel(sublist));
  } else if (model_type == "no denominator") {
    //    model_ = Teuchos::rcp(new NoDenominatorConductivityModel(sublist));
  } else {
    ASSERT(0);
  }

}

OverlandConductivityEvaluator::OverlandConductivityEvaluator(const OverlandConductivityEvaluator& other) :
    SecondaryVariableFieldEvaluator(other),
    depth_key_(other.depth_key_),
    slope_key_(other.slope_key_),
    coef_key_(other.coef_key_),
    dens_key_(other.dens_key_),
    dt_(other.dt_),
    factor_(other.factor_),
    dens_(other.dens_),
    model_(other.model_) {}

Teuchos::RCP<FieldEvaluator>
OverlandConductivityEvaluator::Clone() const {
  return Teuchos::rcp(new OverlandConductivityEvaluator(*this));
}

// Required methods from SecondaryVariableFieldEvaluator
void OverlandConductivityEvaluator::EvaluateField_(const Teuchos::Ptr<State>& S,
        const Teuchos::Ptr<CompositeVector>& result) {

  Teuchos::RCP<const CompositeVector> depth = S->GetFieldData(depth_key_);
  Teuchos::RCP<const CompositeVector> slope = S->GetFieldData(slope_key_);
  Teuchos::RCP<const CompositeVector> coef = S->GetFieldData(coef_key_);

  for (CompositeVector::name_iterator comp=result->begin();
       comp!=result->end(); ++comp) {
    const Epetra_MultiVector& depth_v = *depth->ViewComponent(*comp,false);
    const Epetra_MultiVector& slope_v = *slope->ViewComponent(*comp,false);
    const Epetra_MultiVector& coef_v = *coef->ViewComponent(*comp,false);
    Epetra_MultiVector& result_v = *result->ViewComponent(*comp,false);

    int ncomp = result->size(*comp, false);
    if (dt_) {
      for (int i=0; i!=ncomp; ++i) {
        result_v[0][i] = model_->Conductivity(factor_*depth_v[0][i], slope_v[0][i], coef_v[0][i]);
      }
    } else {
      //      double dt = *S->GetScalarData("dt");
      for (int i=0; i!=ncomp; ++i) {
        result_v[0][i] = model_->Conductivity(depth_v[0][i], slope_v[0][i], coef_v[0][i]);
      }
    }

    if (dens_) {
      const Epetra_MultiVector& dens_v = *S->GetFieldData(dens_key_)->ViewComponent(*comp,false);
      for (int i=0; i!=ncomp; ++i) {
        result_v[0][i] *= dens_v[0][i];
      }
    }
  }
}

void OverlandConductivityEvaluator::EvaluateFieldPartialDerivative_(
    const Teuchos::Ptr<State>& S,
    Key wrt_key, const Teuchos::Ptr<CompositeVector>& result) {

  Teuchos::RCP<const CompositeVector> depth = S->GetFieldData(depth_key_);
  Teuchos::RCP<const CompositeVector> slope = S->GetFieldData(slope_key_);
  Teuchos::RCP<const CompositeVector> coef = S->GetFieldData(coef_key_);

  if (wrt_key == depth_key_) {
    for (CompositeVector::name_iterator comp=result->begin();
         comp!=result->end(); ++comp) {
      const Epetra_MultiVector& depth_v = *depth->ViewComponent(*comp,false);
      const Epetra_MultiVector& slope_v = *slope->ViewComponent(*comp,false);
      const Epetra_MultiVector& coef_v = *coef->ViewComponent(*comp,false);
      Epetra_MultiVector& result_v = *result->ViewComponent(*comp,false);

      int ncomp = result->size(*comp, false);
      if (dt_) {
        //        double dt = *S->GetScalarData("dt");
        for (int i=0; i!=ncomp; ++i) {
          result_v[0][i] = model_->DConductivityDDepth(factor_*depth_v[0][i], slope_v[0][i], coef_v[0][i]) * factor_;
        }
      } else {
        for (int i=0; i!=ncomp; ++i) {
          result_v[0][i] = model_->DConductivityDDepth(depth_v[0][i], slope_v[0][i], coef_v[0][i]);
        }
      }

      if (dens_) {
        const Epetra_MultiVector& dens_v = *S->GetFieldData(dens_key_)->ViewComponent(*comp,false);
        for (int i=0; i!=ncomp; ++i) {
          result_v[0][i] *= dens_v[0][i];
        }
      }
    }

  } else if (wrt_key == dens_key_) {
    ASSERT(dens_);
    for (CompositeVector::name_iterator comp=result->begin();
         comp!=result->end(); ++comp) {
      const Epetra_MultiVector& depth_v = *depth->ViewComponent(*comp,false);
      const Epetra_MultiVector& slope_v = *slope->ViewComponent(*comp,false);
      const Epetra_MultiVector& coef_v = *coef->ViewComponent(*comp,false);
      Epetra_MultiVector& result_v = *result->ViewComponent(*comp,false);

      int ncomp = result->size(*comp, false);
      if (dt_) {
        //        double dt = *S->GetScalarData("dt");
        for (int i=0; i!=ncomp; ++i) {
          result_v[0][i] = model_->Conductivity(factor_*depth_v[0][i], slope_v[0][i], coef_v[0][i]);
        }
      } else {
        for (int i=0; i!=ncomp; ++i) {
          result_v[0][i] = model_->Conductivity(depth_v[0][i], slope_v[0][i], coef_v[0][i]);
        }
      }
    }

  } else {
    ASSERT(0);
  }
}

} //namespace
} //namespace
} //namespace

<|endoftext|>


<commit_before>#include <algorithm>
#include <stack>
#include <variant>

#include "dDNNFTreeDecompositionBuilder.h"

/* Turn a bounded-treewidth circuit c for which a tree decomposition td
 * is provided into a dNNF rooted at root, following the construction in
 * Section 5.1 of https://arxiv.org/pdf/1811.02944 */
dDNNF&& dDNNFTreeDecompositionBuilder::build() && {
  // We make the tree decomposition friendly
  td.makeFriendly(root_id);

  // We look for bags responsible for each variable
  for(bag_t i{0}; i<td.bags.size(); ++i) {
    const auto &b = td.getBag(i);
    if(td.getChildren(i).empty() && b.size()==1 && c.getGateType(*b.begin()) == BooleanGate::IN)
      responsible_bag[*b.begin()] = i;
  }

  // A friendly tree decomposition has leaf bags for every variable
  // nodes. Let's just check that to be safe.
  assert(responsible_bag.size()==c.inputs.size());

  // Create the input and negated input gates
  for(auto g: c.inputs) {
    auto gate = d.setGate(BooleanGate::IN, c.getProb(g));
    auto not_gate = d.setGate(BooleanGate::NOT);
    d.addWire(not_gate, gate);
    input_gate[g]=gate;
    negated_input_gate[g]=not_gate;
  }

  gate_vector_t<dDNNFGate> result_gates = builddDNNF();

  auto result_id = d.setGate("root", BooleanGate::OR);

  for(const auto &p: result_gates) {
    if(p.suspicious.empty() && p.valuation.find(root_id)->second) {
      d.addWire(result_id, p.id);
      break;
    }
  }

  return std::move(d);
}

constexpr bool isStrong(BooleanGate type, bool value)
{
  switch(type) {
    case BooleanGate::OR:
      return value;
    case BooleanGate::AND:
      return !value;
    case BooleanGate::IN:
      return false;
    default:
      return true;
  }
}

static bool isConnectible(const dDNNFTreeDecompositionBuilder::suspicious_t &suspicious,
                          const TreeDecomposition::Bag &b)
{
  for(const auto &g: suspicious) {
    if(b.find(g)==b.end())
      return false;
  }

  return true;
}

dDNNFTreeDecompositionBuilder::gate_vector_t<dDNNFTreeDecompositionBuilder::dDNNFGate> dDNNFTreeDecompositionBuilder::builddDNNFLeaf(
    bag_t bag)
{
  // If the bag is empty, it behaves as if it was not there
  if(td.getBag(bag).size()==0)
    return {};

  // Otherwise, since we have a friendly decomposition, we have a
  // single gate
  auto single_gate = *td.getBag(bag).begin();

  // We check if this bag is responsible for an input variable
  if(c.getGateType(single_gate)==BooleanGate::IN &&
      responsible_bag.find(single_gate)->second==bag)
  {
    // No need to create an extra gate, just point to the variable and
    // negated variable gate; no suspicious gate.
    dDNNFGate pos = { input_gate.find(single_gate)->second,
      {std::make_pair(single_gate,true)},
      {}
    };
    dDNNFGate neg = { negated_input_gate.find(single_gate)->second,
      {std::make_pair(single_gate,false)},
      {}
    };
    return { std::move(pos), std::move(neg) };
  } else {
    gate_vector_t<dDNNFGate> result_gates;

    // We create two TRUE gates (AND gates with no inputs)
    for(auto v: {true, false}) {
      // Optimization: we know the root is set to True, so no need to
      // construct valuations incompatible with this
      if(single_gate==root_id && !v)
        continue;

      suspicious_t suspicious;
      if(isStrong(c.getGateType(single_gate), v))
        suspicious.insert(single_gate);

      result_gates.emplace_back(
          d.setGate(BooleanGate::AND),
          valuation_t{std::make_pair(single_gate, v)},
          std::move(suspicious)
      );
    }

    return result_gates;
  }
}

bool dDNNFTreeDecompositionBuilder::isAlmostValuation(
    const valuation_t &valuation) const
{
  for(const auto &p1: valuation) {
    for(const auto &p2: valuation) {
      if(p1.first==p2.first)
        continue;
      if(!isStrong(c.getGateType(p1.first),p2.second))
        continue;

      if(circuitHasWire(p1.first,p2.first)) {
        switch(c.getGateType(p1.first)) {
          case BooleanGate::AND:
          case BooleanGate::OR:
            if(p1.second!=p2.second)
              return false;
            break;
          case BooleanGate::NOT:
            if(p1.second==p2.second)
              return false;
          default:
            ;
        }
      }
    }
  }

  return true;
}

dDNNFTreeDecompositionBuilder::suspicious_t
dDNNFTreeDecompositionBuilder::getInnocent(
    const valuation_t &valuation,
    const suspicious_t &innocent) const
{
  suspicious_t result = innocent;

  for(const auto &[g1,val]: valuation) {
    if(innocent.find(g1)!=innocent.end())
      continue;

    // We check if it is strong, if not it is innocent
    if(!isStrong(c.getGateType(g1), valuation.find(g1)->second)) {
      result.insert(g1);
      continue;
    }

    // We have a strong gate not innocented by the children bags,
    // it is only innocent if we also have in the bag an input to
    // that gate which is strong for that gate
    for(const auto &[g2, value]: valuation) {
      if(g2==g1)
        continue;

      if(circuitHasWire(g1,g2)) {
        if(isStrong(c.getGateType(g1), value)) {
          result.insert(g1);
          break;
        }
      }
    }
  }

  return result;
}

std::ostream &operator<<(std::ostream &o, const dDNNFTreeDecompositionBuilder::gates_to_or_t &gates_to_or)
{
  for(auto &[valuation, m]: gates_to_or) {
    o << "{";
    bool first=true;
    for(auto &[var, val]: valuation) {
      if(!first)
        o << ",";
      o << "(" << var << "," << val << ")";
      first=false;
    }
    o << "}: ";

    for(auto &[innocent, gates]: m) {
      o << "{";
      bool first=true;
      for(auto &x: innocent) {
        if(!first)
          o << ",";
        o << x;
        first=false;
      }
      o << "} ";
      o << "[";
      first=true;
      for(auto &x: gates) {
        if(!first)
          o << ",";
        o << x;
        first=false;
      }
      o << "] ";
    }

    o << "\n";
  }

  return o;
}

dDNNFTreeDecompositionBuilder::gates_to_or_t dDNNFTreeDecompositionBuilder::collectGatesToOr(
    bag_t bag,
    const gate_vector_t<dDNNFGate> &children_gates,
    const gates_to_or_t &partial)
{
  gates_to_or_t gates_to_or;

  for(auto g: children_gates) {
    // We check all suspicious gates are in the bag of the parent
    if(!isConnectible(g.suspicious,td.getBag(bag)))
      continue;

    // Find all valuations in partial that are compatible with this partial
    // valuation, if it exists
    auto compatibleValuation = [&g](const auto &p) {
                                 for(const auto &[var, val]: p.first) {
                                   auto it = g.valuation.find(var);
                                   if(it != g.valuation.end() && it->second != val)
                                     return false;
                                 }
                                 return true;
                               };

    for (auto it = std::find_if(partial.begin(), partial.end(), compatibleValuation);
         it != partial.end();
         it = std::find_if(std::next(it), partial.end(), compatibleValuation)) {
      auto &[matching_valuation, m] = *it;

      valuation_t valuation = matching_valuation;
      suspicious_t extra_innocent{};
      for(auto &[var, val]: g.valuation) {
         if(td.getBag(bag).find(var)!=td.getBag(bag).end()) {
           if(matching_valuation.find(var)==matching_valuation.end())
             valuation[var]=val;

           if(g.suspicious.find(var)==g.suspicious.end()) {
             extra_innocent.insert(var);
          }
        }
      }

      // We check valuation is still an almost-valuation
      if(!isAlmostValuation(valuation))
        continue;

      for(auto &[innocent, gates]: m) {
        suspicious_t new_innocent = std::move(extra_innocent);

        for(auto s: innocent)
          new_innocent.insert(s);

        new_innocent = getInnocent(valuation, new_innocent);

        if(gates.empty())
          gates_to_or[valuation][new_innocent].push_back(g.id);
        else {
          for(auto g2: gates) {
            gate_t and_gate;

            // We optimize a bit by avoiding creating an AND gate if there
            // is only one child, or if a second child is a TRUE gate
            gate_t gates_children[2];
            unsigned nb = 0;

            if(!(d.getGateType(g.id)==BooleanGate::AND &&
                  d.getWires(g.id).empty()))
              gates_children[nb++]=g.id;

            if(!(d.getGateType(g2)==BooleanGate::AND &&
                  d.getWires(g2).empty()))
              gates_children[nb++]=g2;

            if(nb==0) {
              // We have one (or two) TRUE gates; we just reuse it -- even
              // though we reuse a child gate, connections will still make
              // sense as the valuation and suspicious set been correctly
              // computed
              and_gate = g.id;
            } else if(nb==1) {
              // Only one non-TRUE gate; we reuse it. Similarly as in the
              // previous case, even though we reuse this gate, the connections
              // made from it will still take into account the valuation and
              // suspicious set
              and_gate = gates_children[0];
            } else {
              and_gate = d.setGate(BooleanGate::AND);
              for(auto x: gates_children) {
                d.addWire(and_gate, x);
              }
            }

            gates_to_or[valuation][new_innocent].push_back(and_gate);
          }
        }
      }
    }
  }

  return gates_to_or;
}

dDNNFTreeDecompositionBuilder::gate_vector_t<dDNNFTreeDecompositionBuilder::dDNNFGate> dDNNFTreeDecompositionBuilder::builddDNNF()
{
  // Unfortunately, tree decompositions can be quite deep so we need to
  // simulate recursion with a heap-based stack, to avoid exhausting the
  // actual memory stack
  struct RecursionParams
  {
    bag_t bag;
    size_t children_processed;
    gates_to_or_t gates_to_or;

    RecursionParams(bag_t b, size_t c, gates_to_or_t g) :
      bag(b), children_processed(c), gates_to_or(std::move(g)) {}

    RecursionParams(bag_t b) :
      bag(b), children_processed(0) {
        gates_to_or_t::mapped_type m;
        m[suspicious_t{}] = {};
        gates_to_or[valuation_t{}] = std::move(m);
      }
  };

  using RecursionResult = gate_vector_t<dDNNFGate>;

  std::stack<std::variant<RecursionParams,RecursionResult>> stack;
  stack.emplace(RecursionParams{td.root});

  while(!stack.empty()) {
    RecursionResult result;

    if(stack.top().index()==1) { // RecursionResult
      result = std::move(std::get<1>(stack.top()));
      stack.pop();
      if(stack.empty())
        return result;
    }

    auto [bag, children_processed, gates_to_or] = std::move(std::get<0>(stack.top()));
    stack.pop();

    if(td.getChildren(bag).empty()) {
      auto x = builddDNNFLeaf(bag);
      stack.emplace(x);
    } else {
      if(children_processed>0) {
        gates_to_or = collectGatesToOr(bag, result, gates_to_or);
      }

      if(children_processed==td.getChildren(bag).size()) {
        gate_vector_t<dDNNFGate> result_gates;

        for(auto &[valuation, m]: gates_to_or) {
          for(auto &[innocent, gates]: m) {
            gate_t result_gate;

            assert(gates.size()!=0);

            suspicious_t suspicious;
            for(auto &[var, val]: valuation)
              if(innocent.find(var)==innocent.end())
                suspicious.insert(var);

            if(gates.size()==1)
              result_gate = *gates.begin();
            else {
              result_gate = d.setGate(BooleanGate::OR);
              for(auto &g: gates) {
                d.addWire(result_gate, g);
              }
            }

            result_gates.emplace_back(result_gate, std::move(valuation), std::move(suspicious));
          }
        }

        stack.emplace(std::move(result_gates));
      } else {
        stack.emplace(RecursionParams{bag, children_processed+1, std::move(gates_to_or)});
        stack.emplace(RecursionParams{td.getChildren(bag)[children_processed]});
      }
    }
  }

  // We return from within the while loop, when we hit the last return
  // value
  assert(false);
}

std::ostream &operator<<(std::ostream &o, const dDNNFTreeDecompositionBuilder::dDNNFGate &g)
{
  o << g.id << "; {";
  bool first=true;
  for(const auto &p: g.valuation) {
    if(!first)
      o << ",";
    first=false;
    o << "(" << p.first << "," << p.second << ")";
  }
  o << "}; {";
  first=true;
  for(auto x: g.suspicious) {
    if(!first)
      o << ",";
    first=false;
    o << x;
  }
  o << "}";

  return o;
}

bool dDNNFTreeDecompositionBuilder::circuitHasWire(gate_t f, gate_t t) const
{
  return wiresSet.find(std::make_pair(f,t))!=wiresSet.end();
}
<commit_msg>More explicit constructors<commit_after>#include <algorithm>
#include <stack>
#include <variant>

#include "dDNNFTreeDecompositionBuilder.h"

/* Turn a bounded-treewidth circuit c for which a tree decomposition td
 * is provided into a dNNF rooted at root, following the construction in
 * Section 5.1 of https://arxiv.org/pdf/1811.02944 */
dDNNF&& dDNNFTreeDecompositionBuilder::build() && {
  // We make the tree decomposition friendly
  td.makeFriendly(root_id);

  // We look for bags responsible for each variable
  for(bag_t i{0}; i<td.bags.size(); ++i) {
    const auto &b = td.getBag(i);
    if(td.getChildren(i).empty() && b.size()==1 && c.getGateType(*b.begin()) == BooleanGate::IN)
      responsible_bag[*b.begin()] = i;
  }

  // A friendly tree decomposition has leaf bags for every variable
  // nodes. Let's just check that to be safe.
  assert(responsible_bag.size()==c.inputs.size());

  // Create the input and negated input gates
  for(auto g: c.inputs) {
    auto gate = d.setGate(BooleanGate::IN, c.getProb(g));
    auto not_gate = d.setGate(BooleanGate::NOT);
    d.addWire(not_gate, gate);
    input_gate[g]=gate;
    negated_input_gate[g]=not_gate;
  }

  gate_vector_t<dDNNFGate> result_gates = builddDNNF();

  auto result_id = d.setGate("root", BooleanGate::OR);

  for(const auto &p: result_gates) {
    if(p.suspicious.empty() && p.valuation.find(root_id)->second) {
      d.addWire(result_id, p.id);
      break;
    }
  }

  return std::move(d);
}

constexpr bool isStrong(BooleanGate type, bool value)
{
  switch(type) {
    case BooleanGate::OR:
      return value;
    case BooleanGate::AND:
      return !value;
    case BooleanGate::IN:
      return false;
    default:
      return true;
  }
}

static bool isConnectible(const dDNNFTreeDecompositionBuilder::suspicious_t &suspicious,
                          const TreeDecomposition::Bag &b)
{
  for(const auto &g: suspicious) {
    if(b.find(g)==b.end())
      return false;
  }

  return true;
}

dDNNFTreeDecompositionBuilder::gate_vector_t<dDNNFTreeDecompositionBuilder::dDNNFGate> dDNNFTreeDecompositionBuilder::builddDNNFLeaf(
    bag_t bag)
{
  // If the bag is empty, it behaves as if it was not there
  if(td.getBag(bag).size()==0)
    return {};

  // Otherwise, since we have a friendly decomposition, we have a
  // single gate
  auto single_gate = *td.getBag(bag).begin();

  // We check if this bag is responsible for an input variable
  if(c.getGateType(single_gate)==BooleanGate::IN &&
      responsible_bag.find(single_gate)->second==bag)
  {
    // No need to create an extra gate, just point to the variable and
    // negated variable gate; no suspicious gate.
    dDNNFGate pos = { input_gate.find(single_gate)->second,
      {std::make_pair(single_gate,true)},
      suspicious_t{}
    };
    dDNNFGate neg = { negated_input_gate.find(single_gate)->second,
      {std::make_pair(single_gate,false)},
      suspicious_t{}
    };
    return { std::move(pos), std::move(neg) };
  } else {
    gate_vector_t<dDNNFGate> result_gates;

    // We create two TRUE gates (AND gates with no inputs)
    for(auto v: {true, false}) {
      // Optimization: we know the root is set to True, so no need to
      // construct valuations incompatible with this
      if(single_gate==root_id && !v)
        continue;

      suspicious_t suspicious;
      if(isStrong(c.getGateType(single_gate), v))
        suspicious.insert(single_gate);

      result_gates.emplace_back(
          d.setGate(BooleanGate::AND),
          valuation_t{std::make_pair(single_gate, v)},
          std::move(suspicious)
      );
    }

    return result_gates;
  }
}

bool dDNNFTreeDecompositionBuilder::isAlmostValuation(
    const valuation_t &valuation) const
{
  for(const auto &p1: valuation) {
    for(const auto &p2: valuation) {
      if(p1.first==p2.first)
        continue;
      if(!isStrong(c.getGateType(p1.first),p2.second))
        continue;

      if(circuitHasWire(p1.first,p2.first)) {
        switch(c.getGateType(p1.first)) {
          case BooleanGate::AND:
          case BooleanGate::OR:
            if(p1.second!=p2.second)
              return false;
            break;
          case BooleanGate::NOT:
            if(p1.second==p2.second)
              return false;
          default:
            ;
        }
      }
    }
  }

  return true;
}

dDNNFTreeDecompositionBuilder::suspicious_t
dDNNFTreeDecompositionBuilder::getInnocent(
    const valuation_t &valuation,
    const suspicious_t &innocent) const
{
  suspicious_t result = innocent;

  for(const auto &[g1,val]: valuation) {
    if(innocent.find(g1)!=innocent.end())
      continue;

    // We check if it is strong, if not it is innocent
    if(!isStrong(c.getGateType(g1), valuation.find(g1)->second)) {
      result.insert(g1);
      continue;
    }

    // We have a strong gate not innocented by the children bags,
    // it is only innocent if we also have in the bag an input to
    // that gate which is strong for that gate
    for(const auto &[g2, value]: valuation) {
      if(g2==g1)
        continue;

      if(circuitHasWire(g1,g2)) {
        if(isStrong(c.getGateType(g1), value)) {
          result.insert(g1);
          break;
        }
      }
    }
  }

  return result;
}

std::ostream &operator<<(std::ostream &o, const dDNNFTreeDecompositionBuilder::gates_to_or_t &gates_to_or)
{
  for(auto &[valuation, m]: gates_to_or) {
    o << "{";
    bool first=true;
    for(auto &[var, val]: valuation) {
      if(!first)
        o << ",";
      o << "(" << var << "," << val << ")";
      first=false;
    }
    o << "}: ";

    for(auto &[innocent, gates]: m) {
      o << "{";
      bool first=true;
      for(auto &x: innocent) {
        if(!first)
          o << ",";
        o << x;
        first=false;
      }
      o << "} ";
      o << "[";
      first=true;
      for(auto &x: gates) {
        if(!first)
          o << ",";
        o << x;
        first=false;
      }
      o << "] ";
    }

    o << "\n";
  }

  return o;
}

dDNNFTreeDecompositionBuilder::gates_to_or_t dDNNFTreeDecompositionBuilder::collectGatesToOr(
    bag_t bag,
    const gate_vector_t<dDNNFGate> &children_gates,
    const gates_to_or_t &partial)
{
  gates_to_or_t gates_to_or;

  for(auto g: children_gates) {
    // We check all suspicious gates are in the bag of the parent
    if(!isConnectible(g.suspicious,td.getBag(bag)))
      continue;

    // Find all valuations in partial that are compatible with this partial
    // valuation, if it exists
    auto compatibleValuation = [&g](const auto &p) {
                                 for(const auto &[var, val]: p.first) {
                                   auto it = g.valuation.find(var);
                                   if(it != g.valuation.end() && it->second != val)
                                     return false;
                                 }
                                 return true;
                               };

    for (auto it = std::find_if(partial.begin(), partial.end(), compatibleValuation);
         it != partial.end();
         it = std::find_if(std::next(it), partial.end(), compatibleValuation)) {
      auto &[matching_valuation, m] = *it;

      valuation_t valuation = matching_valuation;
      suspicious_t extra_innocent{};
      for(auto &[var, val]: g.valuation) {
         if(td.getBag(bag).find(var)!=td.getBag(bag).end()) {
           if(matching_valuation.find(var)==matching_valuation.end())
             valuation[var]=val;

           if(g.suspicious.find(var)==g.suspicious.end()) {
             extra_innocent.insert(var);
          }
        }
      }

      // We check valuation is still an almost-valuation
      if(!isAlmostValuation(valuation))
        continue;

      for(auto &[innocent, gates]: m) {
        suspicious_t new_innocent = std::move(extra_innocent);

        for(auto s: innocent)
          new_innocent.insert(s);

        new_innocent = getInnocent(valuation, new_innocent);

        if(gates.empty())
          gates_to_or[valuation][new_innocent].push_back(g.id);
        else {
          for(auto g2: gates) {
            gate_t and_gate;

            // We optimize a bit by avoiding creating an AND gate if there
            // is only one child, or if a second child is a TRUE gate
            gate_t gates_children[2];
            unsigned nb = 0;

            if(!(d.getGateType(g.id)==BooleanGate::AND &&
                  d.getWires(g.id).empty()))
              gates_children[nb++]=g.id;

            if(!(d.getGateType(g2)==BooleanGate::AND &&
                  d.getWires(g2).empty()))
              gates_children[nb++]=g2;

            if(nb==0) {
              // We have one (or two) TRUE gates; we just reuse it -- even
              // though we reuse a child gate, connections will still make
              // sense as the valuation and suspicious set been correctly
              // computed
              and_gate = g.id;
            } else if(nb==1) {
              // Only one non-TRUE gate; we reuse it. Similarly as in the
              // previous case, even though we reuse this gate, the connections
              // made from it will still take into account the valuation and
              // suspicious set
              and_gate = gates_children[0];
            } else {
              and_gate = d.setGate(BooleanGate::AND);
              for(auto x: gates_children) {
                d.addWire(and_gate, x);
              }
            }

            gates_to_or[valuation][new_innocent].push_back(and_gate);
          }
        }
      }
    }
  }

  return gates_to_or;
}

dDNNFTreeDecompositionBuilder::gate_vector_t<dDNNFTreeDecompositionBuilder::dDNNFGate> dDNNFTreeDecompositionBuilder::builddDNNF()
{
  // Unfortunately, tree decompositions can be quite deep so we need to
  // simulate recursion with a heap-based stack, to avoid exhausting the
  // actual memory stack
  struct RecursionParams
  {
    bag_t bag;
    size_t children_processed;
    gates_to_or_t gates_to_or;

    RecursionParams(bag_t b, size_t c, gates_to_or_t g) :
      bag(b), children_processed(c), gates_to_or(std::move(g)) {}

    RecursionParams(bag_t b) :
      bag(b), children_processed(0) {
        gates_to_or_t::mapped_type m;
        m[suspicious_t{}] = {};
        gates_to_or[valuation_t{}] = std::move(m);
      }
  };

  using RecursionResult = gate_vector_t<dDNNFGate>;

  std::stack<std::variant<RecursionParams,RecursionResult>> stack;
  stack.emplace(RecursionParams{td.root});

  while(!stack.empty()) {
    RecursionResult result;

    if(stack.top().index()==1) { // RecursionResult
      result = std::move(std::get<1>(stack.top()));
      stack.pop();
      if(stack.empty())
        return result;
    }

    auto [bag, children_processed, gates_to_or] = std::move(std::get<0>(stack.top()));
    stack.pop();

    if(td.getChildren(bag).empty()) {
      auto x = builddDNNFLeaf(bag);
      stack.emplace(x);
    } else {
      if(children_processed>0) {
        gates_to_or = collectGatesToOr(bag, result, gates_to_or);
      }

      if(children_processed==td.getChildren(bag).size()) {
        gate_vector_t<dDNNFGate> result_gates;

        for(auto &[valuation, m]: gates_to_or) {
          for(auto &[innocent, gates]: m) {
            gate_t result_gate;

            assert(gates.size()!=0);

            suspicious_t suspicious;
            for(auto &[var, val]: valuation)
              if(innocent.find(var)==innocent.end())
                suspicious.insert(var);

            if(gates.size()==1)
              result_gate = *gates.begin();
            else {
              result_gate = d.setGate(BooleanGate::OR);
              for(auto &g: gates) {
                d.addWire(result_gate, g);
              }
            }

            result_gates.emplace_back(result_gate, std::move(valuation), std::move(suspicious));
          }
        }

        stack.emplace(std::move(result_gates));
      } else {
        stack.emplace(RecursionParams{bag, children_processed+1, std::move(gates_to_or)});
        stack.emplace(RecursionParams{td.getChildren(bag)[children_processed]});
      }
    }
  }

  // We return from within the while loop, when we hit the last return
  // value
  assert(false);
}

std::ostream &operator<<(std::ostream &o, const dDNNFTreeDecompositionBuilder::dDNNFGate &g)
{
  o << g.id << "; {";
  bool first=true;
  for(const auto &p: g.valuation) {
    if(!first)
      o << ",";
    first=false;
    o << "(" << p.first << "," << p.second << ")";
  }
  o << "}; {";
  first=true;
  for(auto x: g.suspicious) {
    if(!first)
      o << ",";
    first=false;
    o << x;
  }
  o << "}";

  return o;
}

bool dDNNFTreeDecompositionBuilder::circuitHasWire(gate_t f, gate_t t) const
{
  return wiresSet.find(std::make_pair(f,t))!=wiresSet.end();
}
<|endoftext|>


<commit_before>#include <algorithm>
#include <stack>
#include <variant>

#include "dDNNFTreeDecompositionBuilder.h"

/* Turn a bounded-treewidth circuit c for which a tree decomposition td
 * is provided into a dNNF rooted at root, following the construction in
 * Section 5.1 of https://arxiv.org/pdf/1811.02944 */
dDNNF&& dDNNFTreeDecompositionBuilder::build() && {
  // We make the tree decomposition friendly
  td.makeFriendly(root_id);

  // We look for bags responsible for each variable
  for(bag_t i{0}; i<td.bags.size(); ++i) {
    const auto &b = td.getBag(i);
    if(td.getChildren(i).empty() && b.size()==1 && c.getGateType(*b.begin()) == BooleanGate::IN)
      responsible_bag[*b.begin()] = i;
  }

  // A friendly tree decomposition has leaf bags for every variable
  // nodes. Let's just check that to be safe.
  assert(responsible_bag.size()==c.inputs.size());

  // Create the input and negated input gates
  for(auto g: c.inputs) {
    auto gate = d.setGate(BooleanGate::IN, c.getProb(g));
    auto not_gate = d.setGate(BooleanGate::NOT);
    d.addWire(not_gate, gate);
    input_gate[g]=gate;
    negated_input_gate[g]=not_gate;
  }

  gate_vector_t<dDNNFGate> result_gates = builddDNNF();

  auto result_id = d.setGate("root", BooleanGate::OR);

  for(const auto &p: result_gates) {
    if(p.suspicious.empty() && p.valuation.find(root_id)->second) {
      d.addWire(result_id, p.id);
      break;
    }
  }

  return std::move(d);
}

constexpr bool isStrong(BooleanGate type, bool value)
{
  switch(type) {
    case BooleanGate::OR:
      return value;
    case BooleanGate::AND:
      return !value;
    case BooleanGate::IN:
      return false;
    default:
      return true;
  }
}

static bool isConnectible(const dDNNFTreeDecompositionBuilder::suspicious_t &suspicious,
                          const TreeDecomposition::Bag &b)
{
  for(const auto &g: suspicious) {
    if(b.find(g)==b.end())
      return false;
  }

  return true;
}

dDNNFTreeDecompositionBuilder::gate_vector_t<dDNNFTreeDecompositionBuilder::dDNNFGate> dDNNFTreeDecompositionBuilder::builddDNNFLeaf(
    bag_t bag)
{
  // If the bag is empty, it behaves as if it was not there
  if(td.getBag(bag).size()==0)
    return {};

  // Otherwise, since we have a friendly decomposition, we have a
  // single gate
  auto single_gate = *td.getBag(bag).begin();

  // We check if this bag is responsible for an input variable
  if(c.getGateType(single_gate)==BooleanGate::IN &&
      responsible_bag.find(single_gate)->second==bag)
  {
    // No need to create an extra gate, just point to the variable and
    // negated variable gate; no suspicious gate.
    dDNNFGate pos = { input_gate.find(single_gate)->second,
      {std::make_pair(single_gate,true)},
      suspicious_t{}
    };
    dDNNFGate neg = { negated_input_gate.find(single_gate)->second,
      {std::make_pair(single_gate,false)},
      suspicious_t{}
    };
    return { std::move(pos), std::move(neg) };
  } else {
    gate_vector_t<dDNNFGate> result_gates;

    // We create two TRUE gates (AND gates with no inputs)
    for(auto v: {true, false}) {
      // Optimization: we know the root is set to True, so no need to
      // construct valuations incompatible with this
      if(single_gate==root_id && !v)
        continue;

      suspicious_t suspicious;
      if(isStrong(c.getGateType(single_gate), v))
        suspicious.insert(single_gate);

      result_gates.emplace_back(
          d.setGate(BooleanGate::AND),
          valuation_t{std::make_pair(single_gate, v)},
          std::move(suspicious)
      );
    }

    return result_gates;
  }
}

bool dDNNFTreeDecompositionBuilder::isAlmostValuation(
    const valuation_t &valuation) const
{
  for(const auto &p1: valuation) {
    for(const auto &p2: valuation) {
      if(p1.first==p2.first)
        continue;
      if(!isStrong(c.getGateType(p1.first),p2.second))
        continue;

      if(circuitHasWire(p1.first,p2.first)) {
        switch(c.getGateType(p1.first)) {
          case BooleanGate::AND:
          case BooleanGate::OR:
            if(p1.second!=p2.second)
              return false;
            break;
          case BooleanGate::NOT:
            if(p1.second==p2.second)
              return false;
          default:
            ;
        }
      }
    }
  }

  return true;
}

dDNNFTreeDecompositionBuilder::suspicious_t
dDNNFTreeDecompositionBuilder::getInnocent(
    const valuation_t &valuation,
    const suspicious_t &innocent) const
{
  suspicious_t result = innocent;

  for(const auto &[g1,val]: valuation) {
    if(innocent.find(g1)!=innocent.end())
      continue;

    // We check if it is strong, if not it is innocent
    if(!isStrong(c.getGateType(g1), valuation.find(g1)->second)) {
      result.insert(g1);
      continue;
    }

    // We have a strong gate not innocented by the children bags,
    // it is only innocent if we also have in the bag an input to
    // that gate which is strong for that gate
    for(const auto &[g2, value]: valuation) {
      if(g2==g1)
        continue;

      if(circuitHasWire(g1,g2)) {
        if(isStrong(c.getGateType(g1), value)) {
          result.insert(g1);
          break;
        }
      }
    }
  }

  return result;
}

std::ostream &operator<<(std::ostream &o, const dDNNFTreeDecompositionBuilder::gates_to_or_t &gates_to_or)
{
  for(auto &[valuation, m]: gates_to_or) {
    o << "{";
    bool first=true;
    for(auto &[var, val]: valuation) {
      if(!first)
        o << ",";
      o << "(" << var << "," << val << ")";
      first=false;
    }
    o << "}: ";

    for(auto &[innocent, gates]: m) {
      o << "{";
      bool first=true;
      for(auto &x: innocent) {
        if(!first)
          o << ",";
        o << x;
        first=false;
      }
      o << "} ";
      o << "[";
      first=true;
      for(auto &x: gates) {
        if(!first)
          o << ",";
        o << x;
        first=false;
      }
      o << "] ";
    }

    o << "\n";
  }

  return o;
}

dDNNFTreeDecompositionBuilder::gates_to_or_t dDNNFTreeDecompositionBuilder::collectGatesToOr(
    bag_t bag,
    const gate_vector_t<dDNNFGate> &children_gates,
    const gates_to_or_t &partial)
{
  gates_to_or_t gates_to_or;

  for(auto g: children_gates) {
    // We check all suspicious gates are in the bag of the parent
    if(!isConnectible(g.suspicious,td.getBag(bag)))
      continue;

    // Find all valuations in partial that are compatible with this partial
    // valuation, if it exists
    auto compatibleValuation = [&g](const auto &p) {
                                 for(const auto &[var, val]: p.first) {
                                   auto it = g.valuation.find(var);
                                   if(it != g.valuation.end() && it->second != val)
                                     return false;
                                 }
                                 return true;
                               };

    for (auto it = std::find_if(partial.begin(), partial.end(), compatibleValuation);
         it != partial.end();
         it = std::find_if(std::next(it), partial.end(), compatibleValuation)) {
      auto &[matching_valuation, m] = *it;

      valuation_t valuation = matching_valuation;
      suspicious_t extra_innocent{};
      for(auto &[var, val]: g.valuation) {
         if(td.getBag(bag).find(var)!=td.getBag(bag).end()) {
           if(matching_valuation.find(var)==matching_valuation.end())
             valuation[var]=val;

           if(g.suspicious.find(var)==g.suspicious.end()) {
             extra_innocent.insert(var);
          }
        }
      }

      // We check valuation is still an almost-valuation
      if(!isAlmostValuation(valuation))
        continue;

      for(auto &[innocent, gates]: m) {
        suspicious_t new_innocent = std::move(extra_innocent);

        for(auto s: innocent)
          new_innocent.insert(s);

        new_innocent = getInnocent(valuation, new_innocent);

        if(gates.empty())
          gates_to_or[valuation][new_innocent].push_back(g.id);
        else {
          for(auto g2: gates) {
            gate_t and_gate;

            // We optimize a bit by avoiding creating an AND gate if there
            // is only one child, or if a second child is a TRUE gate
            gate_t gates_children[2];
            unsigned nb = 0;

            if(!(d.getGateType(g.id)==BooleanGate::AND &&
                  d.getWires(g.id).empty()))
              gates_children[nb++]=g.id;

            if(!(d.getGateType(g2)==BooleanGate::AND &&
                  d.getWires(g2).empty()))
              gates_children[nb++]=g2;

            if(nb==0) {
              // We have one (or two) TRUE gates; we just reuse it -- even
              // though we reuse a child gate, connections will still make
              // sense as the valuation and suspicious set been correctly
              // computed
              and_gate = g.id;
            } else if(nb==1) {
              // Only one non-TRUE gate; we reuse it. Similarly as in the
              // previous case, even though we reuse this gate, the connections
              // made from it will still take into account the valuation and
              // suspicious set
              and_gate = gates_children[0];
            } else {
              and_gate = d.setGate(BooleanGate::AND);
              for(auto x: gates_children) {
                d.addWire(and_gate, x);
              }
            }

            gates_to_or[valuation][new_innocent].push_back(and_gate);
          }
        }
      }
    }
  }

  return gates_to_or;
}

dDNNFTreeDecompositionBuilder::gate_vector_t<dDNNFTreeDecompositionBuilder::dDNNFGate> dDNNFTreeDecompositionBuilder::builddDNNF()
{
  // Unfortunately, tree decompositions can be quite deep so we need to
  // simulate recursion with a heap-based stack, to avoid exhausting the
  // actual memory stack
  struct RecursionParams
  {
    bag_t bag;
    size_t children_processed;
    gates_to_or_t gates_to_or;

    RecursionParams(bag_t b, size_t c, gates_to_or_t g) :
      bag(b), children_processed(c), gates_to_or(std::move(g)) {}

    RecursionParams(bag_t b) :
      bag(b), children_processed(0) {
        gates_to_or_t::mapped_type m;
        m[suspicious_t{}] = {};
        gates_to_or[valuation_t{}] = std::move(m);
      }
  };

  using RecursionResult = gate_vector_t<dDNNFGate>;

  std::stack<std::variant<RecursionParams,RecursionResult>> stack;
  stack.emplace(RecursionParams{td.root});

  while(!stack.empty()) {
    RecursionResult result;

    if(stack.top().index()==1) { // RecursionResult
      result = std::move(std::get<1>(stack.top()));
      stack.pop();
      if(stack.empty())
        return result;
    }

    auto [bag, children_processed, gates_to_or] = std::move(std::get<0>(stack.top()));
    stack.pop();

    if(td.getChildren(bag).empty()) {
      auto x = builddDNNFLeaf(bag);
      stack.emplace(x);
    } else {
      if(children_processed>0) {
        gates_to_or = collectGatesToOr(bag, result, gates_to_or);
      }

      if(children_processed==td.getChildren(bag).size()) {
        gate_vector_t<dDNNFGate> result_gates;

        for(auto &[valuation, m]: gates_to_or) {
          for(auto &[innocent, gates]: m) {
            gate_t result_gate;

            assert(gates.size()!=0);

            suspicious_t suspicious;
            for(auto &[var, val]: valuation)
              if(innocent.find(var)==innocent.end())
                suspicious.insert(var);

            if(gates.size()==1)
              result_gate = *gates.begin();
            else {
              result_gate = d.setGate(BooleanGate::OR);
              for(auto &g: gates) {
                d.addWire(result_gate, g);
              }
            }

            result_gates.emplace_back(result_gate, std::move(valuation), std::move(suspicious));
          }
        }

        stack.emplace(std::move(result_gates));
      } else {
        stack.emplace(RecursionParams{bag, children_processed+1, std::move(gates_to_or)});
        stack.emplace(RecursionParams{td.getChildren(bag)[children_processed]});
      }
    }
  }

  // We return from within the while loop, when we hit the last return
  // value
  assert(false);
}

std::ostream &operator<<(std::ostream &o, const dDNNFTreeDecompositionBuilder::dDNNFGate &g)
{
  o << g.id << "; {";
  bool first=true;
  for(const auto &p: g.valuation) {
    if(!first)
      o << ",";
    first=false;
    o << "(" << p.first << "," << p.second << ")";
  }
  o << "}; {";
  first=true;
  for(auto x: g.suspicious) {
    if(!first)
      o << ",";
    first=false;
    o << x;
  }
  o << "}";

  return o;
}

bool dDNNFTreeDecompositionBuilder::circuitHasWire(gate_t f, gate_t t) const
{
  return wiresSet.find(std::make_pair(f,t))!=wiresSet.end();
}
<commit_msg>Fix wrong std::move<commit_after>#include <algorithm>
#include <stack>
#include <variant>

#include "dDNNFTreeDecompositionBuilder.h"

/* Turn a bounded-treewidth circuit c for which a tree decomposition td
 * is provided into a dNNF rooted at root, following the construction in
 * Section 5.1 of https://arxiv.org/pdf/1811.02944 */
dDNNF&& dDNNFTreeDecompositionBuilder::build() && {
  // We make the tree decomposition friendly
  td.makeFriendly(root_id);

  // We look for bags responsible for each variable
  for(bag_t i{0}; i<td.bags.size(); ++i) {
    const auto &b = td.getBag(i);
    if(td.getChildren(i).empty() && b.size()==1 && c.getGateType(*b.begin()) == BooleanGate::IN)
      responsible_bag[*b.begin()] = i;
  }

  // A friendly tree decomposition has leaf bags for every variable
  // nodes. Let's just check that to be safe.
  assert(responsible_bag.size()==c.inputs.size());

  // Create the input and negated input gates
  for(auto g: c.inputs) {
    auto gate = d.setGate(BooleanGate::IN, c.getProb(g));
    auto not_gate = d.setGate(BooleanGate::NOT);
    d.addWire(not_gate, gate);
    input_gate[g]=gate;
    negated_input_gate[g]=not_gate;
  }

  gate_vector_t<dDNNFGate> result_gates = builddDNNF();

  auto result_id = d.setGate("root", BooleanGate::OR);

  for(const auto &p: result_gates) {
    if(p.suspicious.empty() && p.valuation.find(root_id)->second) {
      d.addWire(result_id, p.id);
      break;
    }
  }

  return std::move(d);
}

constexpr bool isStrong(BooleanGate type, bool value)
{
  switch(type) {
    case BooleanGate::OR:
      return value;
    case BooleanGate::AND:
      return !value;
    case BooleanGate::IN:
      return false;
    default:
      return true;
  }
}

static bool isConnectible(const dDNNFTreeDecompositionBuilder::suspicious_t &suspicious,
                          const TreeDecomposition::Bag &b)
{
  for(const auto &g: suspicious) {
    if(b.find(g)==b.end())
      return false;
  }

  return true;
}

dDNNFTreeDecompositionBuilder::gate_vector_t<dDNNFTreeDecompositionBuilder::dDNNFGate> dDNNFTreeDecompositionBuilder::builddDNNFLeaf(
    bag_t bag)
{
  // If the bag is empty, it behaves as if it was not there
  if(td.getBag(bag).size()==0)
    return {};

  // Otherwise, since we have a friendly decomposition, we have a
  // single gate
  auto single_gate = *td.getBag(bag).begin();

  // We check if this bag is responsible for an input variable
  if(c.getGateType(single_gate)==BooleanGate::IN &&
      responsible_bag.find(single_gate)->second==bag)
  {
    // No need to create an extra gate, just point to the variable and
    // negated variable gate; no suspicious gate.
    dDNNFGate pos = { input_gate.find(single_gate)->second,
      {std::make_pair(single_gate,true)},
      suspicious_t{}
    };
    dDNNFGate neg = { negated_input_gate.find(single_gate)->second,
      {std::make_pair(single_gate,false)},
      suspicious_t{}
    };
    return { std::move(pos), std::move(neg) };
  } else {
    gate_vector_t<dDNNFGate> result_gates;

    // We create two TRUE gates (AND gates with no inputs)
    for(auto v: {true, false}) {
      // Optimization: we know the root is set to True, so no need to
      // construct valuations incompatible with this
      if(single_gate==root_id && !v)
        continue;

      suspicious_t suspicious;
      if(isStrong(c.getGateType(single_gate), v))
        suspicious.insert(single_gate);

      result_gates.emplace_back(
          d.setGate(BooleanGate::AND),
          valuation_t{std::make_pair(single_gate, v)},
          std::move(suspicious)
      );
    }

    return result_gates;
  }
}

bool dDNNFTreeDecompositionBuilder::isAlmostValuation(
    const valuation_t &valuation) const
{
  for(const auto &p1: valuation) {
    for(const auto &p2: valuation) {
      if(p1.first==p2.first)
        continue;
      if(!isStrong(c.getGateType(p1.first),p2.second))
        continue;

      if(circuitHasWire(p1.first,p2.first)) {
        switch(c.getGateType(p1.first)) {
          case BooleanGate::AND:
          case BooleanGate::OR:
            if(p1.second!=p2.second)
              return false;
            break;
          case BooleanGate::NOT:
            if(p1.second==p2.second)
              return false;
          default:
            ;
        }
      }
    }
  }

  return true;
}

dDNNFTreeDecompositionBuilder::suspicious_t
dDNNFTreeDecompositionBuilder::getInnocent(
    const valuation_t &valuation,
    const suspicious_t &innocent) const
{
  suspicious_t result = innocent;

  for(const auto &[g1,val]: valuation) {
    if(innocent.find(g1)!=innocent.end())
      continue;

    // We check if it is strong, if not it is innocent
    if(!isStrong(c.getGateType(g1), valuation.find(g1)->second)) {
      result.insert(g1);
      continue;
    }

    // We have a strong gate not innocented by the children bags,
    // it is only innocent if we also have in the bag an input to
    // that gate which is strong for that gate
    for(const auto &[g2, value]: valuation) {
      if(g2==g1)
        continue;

      if(circuitHasWire(g1,g2)) {
        if(isStrong(c.getGateType(g1), value)) {
          result.insert(g1);
          break;
        }
      }
    }
  }

  return result;
}

std::ostream &operator<<(std::ostream &o, const dDNNFTreeDecompositionBuilder::gates_to_or_t &gates_to_or)
{
  for(auto &[valuation, m]: gates_to_or) {
    o << "{";
    bool first=true;
    for(auto &[var, val]: valuation) {
      if(!first)
        o << ",";
      o << "(" << var << "," << val << ")";
      first=false;
    }
    o << "}: ";

    for(auto &[innocent, gates]: m) {
      o << "{";
      bool first=true;
      for(auto &x: innocent) {
        if(!first)
          o << ",";
        o << x;
        first=false;
      }
      o << "} ";
      o << "[";
      first=true;
      for(auto &x: gates) {
        if(!first)
          o << ",";
        o << x;
        first=false;
      }
      o << "] ";
    }

    o << "\n";
  }

  return o;
}

dDNNFTreeDecompositionBuilder::gates_to_or_t dDNNFTreeDecompositionBuilder::collectGatesToOr(
    bag_t bag,
    const gate_vector_t<dDNNFGate> &children_gates,
    const gates_to_or_t &partial)
{
  gates_to_or_t gates_to_or;

  for(auto g: children_gates) {
    // We check all suspicious gates are in the bag of the parent
    if(!isConnectible(g.suspicious,td.getBag(bag)))
      continue;

    // Find all valuations in partial that are compatible with this partial
    // valuation, if it exists
    auto compatibleValuation = [&g](const auto &p) {
                                 for(const auto &[var, val]: p.first) {
                                   auto it = g.valuation.find(var);
                                   if(it != g.valuation.end() && it->second != val)
                                     return false;
                                 }
                                 return true;
                               };

    for (auto it = std::find_if(partial.begin(), partial.end(), compatibleValuation);
         it != partial.end();
         it = std::find_if(std::next(it), partial.end(), compatibleValuation)) {
      auto &[matching_valuation, m] = *it;

      valuation_t valuation = matching_valuation;
      suspicious_t extra_innocent{};
      for(auto &[var, val]: g.valuation) {
         if(td.getBag(bag).find(var)!=td.getBag(bag).end()) {
           if(matching_valuation.find(var)==matching_valuation.end())
             valuation[var]=val;

           if(g.suspicious.find(var)==g.suspicious.end()) {
             extra_innocent.insert(var);
          }
        }
      }

      // We check valuation is still an almost-valuation
      if(!isAlmostValuation(valuation))
        continue;

      for(auto &[innocent, gates]: m) {
        suspicious_t new_innocent = extra_innocent;

        for(auto s: innocent)
          new_innocent.insert(s);

        new_innocent = getInnocent(valuation, new_innocent);

        if(gates.empty())
          gates_to_or[valuation][new_innocent].push_back(g.id);
        else {
          for(auto g2: gates) {
            gate_t and_gate;

            // We optimize a bit by avoiding creating an AND gate if there
            // is only one child, or if a second child is a TRUE gate
            gate_t gates_children[2];
            unsigned nb = 0;

            if(!(d.getGateType(g.id)==BooleanGate::AND &&
                  d.getWires(g.id).empty()))
              gates_children[nb++]=g.id;

            if(!(d.getGateType(g2)==BooleanGate::AND &&
                  d.getWires(g2).empty()))
              gates_children[nb++]=g2;

            if(nb==0) {
              // We have one (or two) TRUE gates; we just reuse it -- even
              // though we reuse a child gate, connections will still make
              // sense as the valuation and suspicious set been correctly
              // computed
              and_gate = g.id;
            } else if(nb==1) {
              // Only one non-TRUE gate; we reuse it. Similarly as in the
              // previous case, even though we reuse this gate, the connections
              // made from it will still take into account the valuation and
              // suspicious set
              and_gate = gates_children[0];
            } else {
              and_gate = d.setGate(BooleanGate::AND);
              for(auto x: gates_children) {
                d.addWire(and_gate, x);
              }
            }

            gates_to_or[valuation][new_innocent].push_back(and_gate);
          }
        }
      }
    }
  }

  return gates_to_or;
}

dDNNFTreeDecompositionBuilder::gate_vector_t<dDNNFTreeDecompositionBuilder::dDNNFGate> dDNNFTreeDecompositionBuilder::builddDNNF()
{
  // Unfortunately, tree decompositions can be quite deep so we need to
  // simulate recursion with a heap-based stack, to avoid exhausting the
  // actual memory stack
  struct RecursionParams
  {
    bag_t bag;
    size_t children_processed;
    gates_to_or_t gates_to_or;

    RecursionParams(bag_t b, size_t c, gates_to_or_t g) :
      bag(b), children_processed(c), gates_to_or(std::move(g)) {}

    RecursionParams(bag_t b) :
      bag(b), children_processed(0) {
        gates_to_or_t::mapped_type m;
        m[suspicious_t{}] = {};
        gates_to_or[valuation_t{}] = std::move(m);
      }
  };

  using RecursionResult = gate_vector_t<dDNNFGate>;

  std::stack<std::variant<RecursionParams,RecursionResult>> stack;
  stack.emplace(RecursionParams{td.root});

  while(!stack.empty()) {
    RecursionResult result;

    if(stack.top().index()==1) { // RecursionResult
      result = std::move(std::get<1>(stack.top()));
      stack.pop();
      if(stack.empty())
        return result;
    }

    auto [bag, children_processed, gates_to_or] = std::move(std::get<0>(stack.top()));
    stack.pop();

    if(td.getChildren(bag).empty()) {
      auto x = builddDNNFLeaf(bag);
      stack.emplace(x);
    } else {
      if(children_processed>0) {
        gates_to_or = collectGatesToOr(bag, result, gates_to_or);
      }

      if(children_processed==td.getChildren(bag).size()) {
        gate_vector_t<dDNNFGate> result_gates;

        for(auto &[valuation, m]: gates_to_or) {
          for(auto &[innocent, gates]: m) {
            gate_t result_gate;

            assert(gates.size()!=0);

            suspicious_t suspicious;
            for(auto &[var, val]: valuation)
              if(innocent.find(var)==innocent.end())
                suspicious.insert(var);

            if(gates.size()==1)
              result_gate = *gates.begin();
            else {
              result_gate = d.setGate(BooleanGate::OR);
              for(auto &g: gates) {
                d.addWire(result_gate, g);
              }
            }

            result_gates.emplace_back(result_gate, std::move(valuation), std::move(suspicious));
          }
        }

        stack.emplace(std::move(result_gates));
      } else {
        stack.emplace(RecursionParams{bag, children_processed+1, std::move(gates_to_or)});
        stack.emplace(RecursionParams{td.getChildren(bag)[children_processed]});
      }
    }
  }

  // We return from within the while loop, when we hit the last return
  // value
  assert(false);
}

std::ostream &operator<<(std::ostream &o, const dDNNFTreeDecompositionBuilder::dDNNFGate &g)
{
  o << g.id << "; {";
  bool first=true;
  for(const auto &p: g.valuation) {
    if(!first)
      o << ",";
    first=false;
    o << "(" << p.first << "," << p.second << ")";
  }
  o << "}; {";
  first=true;
  for(auto x: g.suspicious) {
    if(!first)
      o << ",";
    first=false;
    o << x;
  }
  o << "}";

  return o;
}

bool dDNNFTreeDecompositionBuilder::circuitHasWire(gate_t f, gate_t t) const
{
  return wiresSet.find(std::make_pair(f,t))!=wiresSet.end();
}
<|endoftext|>