model.h 6.49 KB
Newer Older
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
// Copyright 2010-2018 Google LLC
// 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.

#ifndef OR_TOOLS_SAT_MODEL_H_
#define OR_TOOLS_SAT_MODEL_H_

#include <cstddef>
#include <functional>
#include <map>
#include <memory>
#include <vector>

#include "absl/container/flat_hash_map.h"
#include "ortools/base/logging.h"
#include "ortools/base/macros.h"
#include "ortools/base/map_util.h"
#include "ortools/base/typeid.h"

namespace operations_research {
namespace sat {

/**
 * Class that owns everything related to a particular optimization model.
 *
 * This class is actually a fully generic wrapper that can hold any type of
 * constraints, watchers, solvers and provide a mecanism to wire them together.
 */
class Model {
 public:
  Model() {}

42 43 44 45 46 47
  /**
   * When there is more than one model in an application, it makes sense to
   * name them for debugging or logging.
   */
  explicit Model(std::string name) : name_(name) {}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
  /**
   * This makes it possible  to have a nicer API on the client side, and it
   * allows both of these forms:
   *   - ConstraintCreationFunction(contraint_args, &model);
   *   - model.Add(ConstraintCreationFunction(contraint_args));
   *
   * The second form is a bit nicer for the client and it also allows to store
   * constraints and add them later. However, the function creating the
   * constraint is slighly more involved.
   *
   * \code
   std::function<void(Model*)> ConstraintCreationFunction(contraint_args) {
     return [=] (Model* model) {
        ... the same code ...
     };
   }
   \endcode
   *
   * We also have a templated return value for the functions that need it like
   * \code
   const BooleanVariable b = model.Add(NewBooleanVariable());
   const IntegerVariable i = model.Add(NewWeightedSum(weights, variables));
   \endcode
   */
  template <typename T>
  T Add(std::function<T(Model*)> f) {
    return f(this);
  }

  /// Similar to Add() but this is const.
  template <typename T>
  T Get(std::function<T(const Model&)> f) const {
    return f(*this);
  }

  /**
   * Returns an object of type T that is unique to this model (like a "local"
   * singleton). This returns an already created instance or create a new one if
   * needed using the T(Model* model) constructor if it exist or T() otherwise.
   *
   * This works a bit like in a dependency injection framework and allows to
   * really easily wire all the classes that make up a solver together. For
   * instance a constraint can depends on the LiteralTrail, or the IntegerTrail
   * or both, it can depend on a Watcher class to register itself in order to
   * be called when needed and so on.
   *
   * IMPORTANT: the Model* constructor functions shouldn't form a cycle between
   * each other, otherwise this will crash the program.
   */
  template <typename T>
  T* GetOrCreate() {
    const size_t type_id = gtl::FastTypeId<T>();
    auto find = singletons_.find(type_id);
    if (find != singletons_.end()) {
      return static_cast<T*>(find->second);
    }

    // New element.
    // TODO(user): directly store std::unique_ptr<> in singletons_?
    T* new_t = MyNew<T>(0);
    singletons_[type_id] = new_t;
    TakeOwnership(new_t);
    return new_t;
  }

  /**
   * Likes GetOrCreate() but do not create the object if it is non-existing.
   *
   * This returns a const version of the object.
   */
  template <typename T>
  const T* Get() const {
    return static_cast<const T*>(
        gtl::FindWithDefault(singletons_, gtl::FastTypeId<T>(), nullptr));
  }

  /**
   * Same as Get(), but returns a mutable version of the object.
   */
  template <typename T>
  T* Mutable() const {
    return static_cast<T*>(
        gtl::FindWithDefault(singletons_, gtl::FastTypeId<T>(), nullptr));
  }

  /**
   * Gives ownership of a pointer to this model.
   *
   * It will be destroyed when the model is.
   */
  template <typename T>
  void TakeOwnership(T* t) {
    cleanup_list_.emplace_back(new Delete<T>(t));
  }

  /**
   * This returns a non-singleton object owned by the model and created with the
   * T(Model* model) constructor if it exist or the T() constructor otherwise.
   * It is just a shortcut to new + TakeOwnership().
   */
  template <typename T>
  T* Create() {
    T* new_t = MyNew<T>(0);
    TakeOwnership(new_t);
    return new_t;
  }

  /**
   * Register a non-owned class that will be "singleton" in the model.
   *
   * It is an error to call this on an already registered class.
   */
  template <typename T>
  void Register(T* non_owned_class) {
    const size_t type_id = gtl::FastTypeId<T>();
    CHECK(!gtl::ContainsKey(singletons_, type_id));
    singletons_[type_id] = non_owned_class;
  }

167 168
  const std::string& Name() const { return name_; }

Valentin Platzgummer's avatar
Valentin Platzgummer committed
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
 private:
  // We want to call the constructor T(model*) if it exists or just T() if
  // it doesn't. For this we use some template "magic":
  // - The first MyNew() will only be defined if the type in decltype() exist.
  // - The second MyNew() will always be defined, but because of the ellipsis
  //   it has lower priority that the first one.
  template <typename T>
  decltype(T(static_cast<Model*>(nullptr)))* MyNew(int) {
    return new T(this);
  }
  template <typename T>
  T* MyNew(...) {
    return new T();
  }

184 185
  const std::string name_;

Valentin Platzgummer's avatar
Valentin Platzgummer committed
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
  // Map of FastTypeId<T> to a "singleton" of type T.
#if defined(__APPLE__)
  std::map</*typeid*/ size_t, void*> singletons_;
#else
  absl::flat_hash_map</*typeid*/ size_t, void*> singletons_;
#endif

  struct DeleteInterface {
    virtual ~DeleteInterface() = default;
  };
  template <typename T>
  class Delete : public DeleteInterface {
   public:
    explicit Delete(T* t) : to_delete_(t) {}
    ~Delete() override = default;

   private:
    std::unique_ptr<T> to_delete_;
  };

  // The list of items to delete.
  //
  // TODO(user): I don't think we need the two layers of unique_ptr, but we
  // don't care too much about efficiency here and this was easier to get
  // working.
  std::vector<std::unique_ptr<DeleteInterface>> cleanup_list_;

  DISALLOW_COPY_AND_ASSIGN(Model);
};

}  // namespace sat
}  // namespace operations_research

#endif  // OR_TOOLS_SAT_MODEL_H_