dither 0.1.5 → 0.2.0.rc3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +6 -1
- data/README.md +3 -0
- data/Rakefile +4 -0
- data/dither.gemspec +4 -0
- data/ext/dither/README.md +2 -0
- data/ext/dither/base_constraint_handler.h +36 -0
- data/ext/dither/combinations.h +127 -0
- data/ext/dither/dither.cc +47 -0
- data/ext/dither/dither.h +31 -0
- data/ext/dither/dither_types.h +32 -0
- data/ext/dither/extconf.rb +4 -0
- data/ext/dither/ipog.cc +451 -0
- data/ext/dither/ipog.h +128 -0
- data/ext/dither/simple_constraint_handler.cc +119 -0
- data/ext/dither/simple_constraint_handler.h +38 -0
- data/lib/dither/api.rb +20 -0
- data/lib/dither/version.rb +1 -1
- data/lib/dither.rb +64 -13
- data/spec/dither/dither_spec.rb +27 -96
- metadata +47 -12
- data/lib/dither/ipog.rb +0 -58
- data/lib/dither/ipog_helper.rb +0 -161
- data/lib/dither/mipog.rb +0 -85
- data/lib/dither/param.rb +0 -19
- data/lib/dither/test_case.rb +0 -80
- data/lib/dither/unbound_param.rb +0 -17
    
        data/ext/dither/ipog.cc
    ADDED
    
    | @@ -0,0 +1,451 @@ | |
| 1 | 
            +
            /*
         | 
| 2 | 
            +
             *
         | 
| 3 | 
            +
             * Copyright (C) 2015 Jason Gowan
         | 
| 4 | 
            +
             * All rights reserved.
         | 
| 5 | 
            +
             *
         | 
| 6 | 
            +
             * This software may be modified and distributed under the terms
         | 
| 7 | 
            +
             * of the BSD license.  See the LICENSE file for details.
         | 
| 8 | 
            +
             */
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            #include "ipog.h"
         | 
| 11 | 
            +
            #include <forward_list>
         | 
| 12 | 
            +
            #include <iterator>
         | 
| 13 | 
            +
            #include <utility>
         | 
| 14 | 
            +
            #include <tuple>
         | 
| 15 | 
            +
            #include <limits.h>
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            namespace dither {
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            Ipog::~Ipog() {
         | 
| 20 | 
            +
              if(constraint_handler != NULL) {
         | 
| 21 | 
            +
                delete constraint_handler;
         | 
| 22 | 
            +
              }
         | 
| 23 | 
            +
            }
         | 
| 24 | 
            +
             | 
| 25 | 
            +
            Ipog::Ipog() {
         | 
| 26 | 
            +
              t_ = 2;
         | 
| 27 | 
            +
            }
         | 
| 28 | 
            +
             | 
| 29 | 
            +
            Ipog::Ipog(const unsigned int t) {
         | 
| 30 | 
            +
              t_ = t;
         | 
| 31 | 
            +
            }
         | 
| 32 | 
            +
             | 
| 33 | 
            +
            void Ipog::init_bound() {
         | 
| 34 | 
            +
              using dither::product;
         | 
| 35 | 
            +
              dtest_case tmp;
         | 
| 36 | 
            +
             | 
| 37 | 
            +
              product(bound_, tmp, input_params_.begin(), input_params_.begin() + t_);
         | 
| 38 | 
            +
              auto size = param_cache_.size();
         | 
| 39 | 
            +
              for (auto it = bound_.begin(); it != bound_.end(); ++it) {
         | 
| 40 | 
            +
                (*it).resize(size, -1);
         | 
| 41 | 
            +
                (*it).shrink_to_fit();
         | 
| 42 | 
            +
              }
         | 
| 43 | 
            +
              bound_.remove_if([this](dtest_case& a) { return has_previously_tested(a); });
         | 
| 44 | 
            +
             | 
| 45 | 
            +
              merge_scratch_.resize(param_cache_.size(), -1);
         | 
| 46 | 
            +
             | 
| 47 | 
            +
              if(constraints.size() > 0) {
         | 
| 48 | 
            +
                for(auto it = param_cache_.cbegin(); it != param_cache_.cend(); ++it) {
         | 
| 49 | 
            +
                  const dval tmp = (*it).size() - 1;
         | 
| 50 | 
            +
                  ranges.push_back(tmp);
         | 
| 51 | 
            +
                }
         | 
| 52 | 
            +
                constraint_handler = new SimpleConstraintHandler(ranges, constraints);
         | 
| 53 | 
            +
              } else {
         | 
| 54 | 
            +
                constraint_handler = new BaseConstraintHandler();
         | 
| 55 | 
            +
              }
         | 
| 56 | 
            +
            }
         | 
| 57 | 
            +
             | 
| 58 | 
            +
            std::forward_list<std::vector<param>> Ipog::cover(const int k) {
         | 
| 59 | 
            +
              std::vector<int> input(k);
         | 
| 60 | 
            +
              for (std::size_t i = 0; i < k; i++) {
         | 
| 61 | 
            +
                input[i] = i;
         | 
| 62 | 
            +
              }
         | 
| 63 | 
            +
             | 
| 64 | 
            +
              std::forward_list<std::vector<int>> output;
         | 
| 65 | 
            +
              combinations(t_ - 1, input, output);
         | 
| 66 | 
            +
              std::forward_list<std::vector<std::vector<param>>> product_input;
         | 
| 67 | 
            +
              const std::vector<param> k_param = param_cache_[k];
         | 
| 68 | 
            +
              for (auto it = output.begin(); it != output.end(); ++it) {
         | 
| 69 | 
            +
                std::vector<std::vector<param>> param_tuple;
         | 
| 70 | 
            +
                (*it).push_back(k);
         | 
| 71 | 
            +
                for (auto iit = (*it).begin(); iit != (*it).end(); ++iit) {
         | 
| 72 | 
            +
                  auto vals = param_cache_[*iit];
         | 
| 73 | 
            +
                  param_tuple.push_back(vals);
         | 
| 74 | 
            +
                }
         | 
| 75 | 
            +
                product_input.push_front(param_tuple);
         | 
| 76 | 
            +
              }
         | 
| 77 | 
            +
             | 
| 78 | 
            +
              std::forward_list<std::vector<param>> coverage;
         | 
| 79 | 
            +
              for (auto it = product_input.begin(); it != product_input.end(); ++it) {
         | 
| 80 | 
            +
                std::vector<param> tmp;
         | 
| 81 | 
            +
                std::vector<std::vector<param>> x = *it;
         | 
| 82 | 
            +
                product2(coverage, tmp, (*it).cbegin(), (*it).cend());
         | 
| 83 | 
            +
              }
         | 
| 84 | 
            +
              coverage.remove_if([this](std::vector<param>& a) { return has_previously_tested(a); });
         | 
| 85 | 
            +
              return coverage;
         | 
| 86 | 
            +
            }
         | 
| 87 | 
            +
             | 
| 88 | 
            +
            void Ipog::run() {
         | 
| 89 | 
            +
              init_bound();
         | 
| 90 | 
            +
              for (auto k = t_; k < input_params_.size(); k++) {
         | 
| 91 | 
            +
                std::forward_list<std::vector<param>> pi = cover(k);
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                {
         | 
| 94 | 
            +
                  auto prev = bound_.cbefore_begin();
         | 
| 95 | 
            +
                  auto next = bound_.begin();
         | 
| 96 | 
            +
                  auto end = bound_.cend();
         | 
| 97 | 
            +
                  while (next != end) {
         | 
| 98 | 
            +
                    if (maximize_coverage(k, *next, pi) == -1) {
         | 
| 99 | 
            +
                      next = bound_.erase_after(prev);
         | 
| 100 | 
            +
                    } else {
         | 
| 101 | 
            +
                      ++prev;
         | 
| 102 | 
            +
                      ++next;
         | 
| 103 | 
            +
                    }
         | 
| 104 | 
            +
                  }
         | 
| 105 | 
            +
                }
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                /* vertical extension */
         | 
| 108 | 
            +
                for (auto pairs = pi.cbegin(); pairs != pi.cend(); ++pairs) {
         | 
| 109 | 
            +
                  const std::vector<param> &test_case = *pairs;
         | 
| 110 | 
            +
                  bool case_covered = constraint_handler->violate_constraints(test_case);
         | 
| 111 | 
            +
             | 
| 112 | 
            +
            			if (!case_covered) {
         | 
| 113 | 
            +
            				bool is_merged = false;
         | 
| 114 | 
            +
            				auto prev = unbound_.before_begin();
         | 
| 115 | 
            +
            				auto next = unbound_.begin();
         | 
| 116 | 
            +
            				auto end = unbound_.end();
         | 
| 117 | 
            +
            				while (next != end) {
         | 
| 118 | 
            +
            					const int merge_result = merge(k, *next, test_case);
         | 
| 119 | 
            +
            					if (merge_result == 0) {
         | 
| 120 | 
            +
            						bound_.push_front(*next);
         | 
| 121 | 
            +
            						unbound_.erase_after(prev);
         | 
| 122 | 
            +
            						is_merged = true;
         | 
| 123 | 
            +
            						break;
         | 
| 124 | 
            +
            					} else if (merge_result == 1) {
         | 
| 125 | 
            +
            						is_merged = true;
         | 
| 126 | 
            +
            						break;
         | 
| 127 | 
            +
            					}
         | 
| 128 | 
            +
            					++prev;
         | 
| 129 | 
            +
            					++next;
         | 
| 130 | 
            +
            				}
         | 
| 131 | 
            +
             | 
| 132 | 
            +
            				if (!is_merged) {
         | 
| 133 | 
            +
            					dtest_case unbound_test_case(param_cache_.size(), -1);
         | 
| 134 | 
            +
            					for (auto it = test_case.cbegin(); it != test_case.cend(); ++it) {
         | 
| 135 | 
            +
            						unbound_test_case[(*it).first] = (*it).second;
         | 
| 136 | 
            +
            					}
         | 
| 137 | 
            +
            					if (!constraint_handler->violate_constraints(unbound_test_case)) {
         | 
| 138 | 
            +
            						unbound_.push_front(unbound_test_case);
         | 
| 139 | 
            +
            					}
         | 
| 140 | 
            +
            				}
         | 
| 141 | 
            +
            			}
         | 
| 142 | 
            +
             | 
| 143 | 
            +
                }
         | 
| 144 | 
            +
              }
         | 
| 145 | 
            +
              ground_solutions();
         | 
| 146 | 
            +
            }
         | 
| 147 | 
            +
             | 
| 148 | 
            +
            /* -1 no merge, 0 perfect merge (no unbound), 1 partial merge */
         | 
| 149 | 
            +
            inline const int Ipog::merge(const int k, dtest_case &test_case,
         | 
| 150 | 
            +
                const std::vector<param> &pairs) {
         | 
| 151 | 
            +
              for (auto it = pairs.cbegin(); it != pairs.cend(); ++it) {
         | 
| 152 | 
            +
                auto value = test_case[(*it).first];
         | 
| 153 | 
            +
                if (!(value == -1 || value == (*it).second)) {
         | 
| 154 | 
            +
                  return -1;
         | 
| 155 | 
            +
                }
         | 
| 156 | 
            +
              }
         | 
| 157 | 
            +
             | 
| 158 | 
            +
              std::copy(test_case.cbegin(), test_case.cend(), merge_scratch_.begin());
         | 
| 159 | 
            +
             | 
| 160 | 
            +
              for (auto it = pairs.cbegin(); it != pairs.cend(); ++it) {
         | 
| 161 | 
            +
                merge_scratch_[(*it).first] = (*it).second;
         | 
| 162 | 
            +
              }
         | 
| 163 | 
            +
             | 
| 164 | 
            +
              if (constraint_handler->violate_constraints(merge_scratch_)) {
         | 
| 165 | 
            +
                return -1;
         | 
| 166 | 
            +
              }
         | 
| 167 | 
            +
             | 
| 168 | 
            +
              for (auto it = pairs.cbegin(); it != pairs.cend(); ++it) {
         | 
| 169 | 
            +
                test_case[(*it).first] = (*it).second;
         | 
| 170 | 
            +
              }
         | 
| 171 | 
            +
             | 
| 172 | 
            +
              for (auto i = 0; i < k; i++) {
         | 
| 173 | 
            +
                if (test_case[i] == -1) {
         | 
| 174 | 
            +
                  return 1;
         | 
| 175 | 
            +
                }
         | 
| 176 | 
            +
              }
         | 
| 177 | 
            +
             | 
| 178 | 
            +
              return 0;
         | 
| 179 | 
            +
            }
         | 
| 180 | 
            +
             | 
| 181 | 
            +
            inline bool Ipog::is_covered(const dtest_case &test_case,
         | 
| 182 | 
            +
                const std::vector<param> ¶ms) {
         | 
| 183 | 
            +
              for (auto param = params.cbegin(); param != params.cend(); ++param) {
         | 
| 184 | 
            +
                if (test_case[(*param).first] != (*param).second) {
         | 
| 185 | 
            +
                  return false;
         | 
| 186 | 
            +
                }
         | 
| 187 | 
            +
              }
         | 
| 188 | 
            +
              return true;
         | 
| 189 | 
            +
            }
         | 
| 190 | 
            +
             | 
| 191 | 
            +
            const int Ipog::maximize_coverage(const int k, dtest_case &test_case,
         | 
| 192 | 
            +
                std::forward_list<std::vector<param>> &pi) {
         | 
| 193 | 
            +
              const std::vector<param> ¶m_range = param_cache_[k];
         | 
| 194 | 
            +
              int current_max = -1;
         | 
| 195 | 
            +
              param max_param = param_range[0];
         | 
| 196 | 
            +
              std::forward_list<std::forward_list<std::vector<param>>::iterator> covered;
         | 
| 197 | 
            +
             | 
| 198 | 
            +
              for (auto it = param_range.cbegin(); it != param_range.cend(); ++it) {
         | 
| 199 | 
            +
                std::forward_list<std::forward_list<std::vector<param>>::iterator>
         | 
| 200 | 
            +
                  tmp_covered;
         | 
| 201 | 
            +
                const param current_param = *it;
         | 
| 202 | 
            +
             | 
| 203 | 
            +
                test_case[current_param.first] = current_param.second;
         | 
| 204 | 
            +
                if (!constraint_handler->violate_constraints(test_case)) {
         | 
| 205 | 
            +
                  int count = 0;
         | 
| 206 | 
            +
                  auto prev = pi.before_begin();
         | 
| 207 | 
            +
                  for (auto params = pi.cbegin(); params != pi.cend(); ++params) {
         | 
| 208 | 
            +
                    if (is_covered(test_case, *params)) {
         | 
| 209 | 
            +
                      tmp_covered.push_front(prev);
         | 
| 210 | 
            +
                      count++;
         | 
| 211 | 
            +
                    }
         | 
| 212 | 
            +
                    ++prev;
         | 
| 213 | 
            +
                  }
         | 
| 214 | 
            +
             | 
| 215 | 
            +
                  if (count > current_max) {
         | 
| 216 | 
            +
                    current_max = count;
         | 
| 217 | 
            +
                    max_param = current_param;
         | 
| 218 | 
            +
                    covered = tmp_covered;
         | 
| 219 | 
            +
                  }
         | 
| 220 | 
            +
                }
         | 
| 221 | 
            +
                test_case[current_param.first] = -1;
         | 
| 222 | 
            +
              }
         | 
| 223 | 
            +
             | 
| 224 | 
            +
              if (current_max == -1) {
         | 
| 225 | 
            +
                return -1;
         | 
| 226 | 
            +
              }
         | 
| 227 | 
            +
             | 
| 228 | 
            +
              /* remove covered */
         | 
| 229 | 
            +
              for (auto it = covered.begin(); it != covered.end(); ++it) {
         | 
| 230 | 
            +
                pi.erase_after(*it);
         | 
| 231 | 
            +
              }
         | 
| 232 | 
            +
             | 
| 233 | 
            +
              test_case[max_param.first] = max_param.second;
         | 
| 234 | 
            +
              return current_max;
         | 
| 235 | 
            +
            }
         | 
| 236 | 
            +
             | 
| 237 | 
            +
            void Ipog::display_raw_solution() {
         | 
| 238 | 
            +
              /* display_header(); */
         | 
| 239 | 
            +
              for (auto it = bound_.cbegin(); it != bound_.cend(); ++it) {
         | 
| 240 | 
            +
                display_test_case(*it);
         | 
| 241 | 
            +
              }
         | 
| 242 | 
            +
            }
         | 
| 243 | 
            +
             | 
| 244 | 
            +
            void Ipog::add_parameter(const std::string name, const int *terms,
         | 
| 245 | 
            +
                const unsigned int terms_length) {
         | 
| 246 | 
            +
              std::vector<int> tmp(terms, terms + terms_length);
         | 
| 247 | 
            +
              std::pair<std::string, std::vector<int>> key_value(name, tmp);
         | 
| 248 | 
            +
              int_params_.insert(key_value);
         | 
| 249 | 
            +
              ordered_param_names_.push_back(name);
         | 
| 250 | 
            +
            }
         | 
| 251 | 
            +
             | 
| 252 | 
            +
            void Ipog::add_parameter(const std::string name, const std::string *terms,
         | 
| 253 | 
            +
                const unsigned int terms_length) {
         | 
| 254 | 
            +
              std::vector<std::string> tmp(terms, terms + terms_length);
         | 
| 255 | 
            +
              std::pair<std::string, std::vector<std::string>> key_value(name, tmp);
         | 
| 256 | 
            +
              str_params_.insert(key_value);
         | 
| 257 | 
            +
              ordered_param_names_.push_back(name);
         | 
| 258 | 
            +
            }
         | 
| 259 | 
            +
             | 
| 260 | 
            +
            void Ipog::add_parameter(const std::string name) {
         | 
| 261 | 
            +
              const bool arr[] = {true, false};
         | 
| 262 | 
            +
              std::vector<bool> tmp(arr, arr + 2);
         | 
| 263 | 
            +
              std::pair<std::string, std::vector<bool>> key_value(name, tmp);
         | 
| 264 | 
            +
              bool_params_.insert(key_value);
         | 
| 265 | 
            +
              ordered_param_names_.push_back(name);
         | 
| 266 | 
            +
            }
         | 
| 267 | 
            +
             | 
| 268 | 
            +
            void Ipog::init_param_cache() {
         | 
| 269 | 
            +
              std::vector<std::tuple<std::string, int, int>> tmp;
         | 
| 270 | 
            +
              for (auto it = int_params_.cbegin(); it != int_params_.cend(); ++it) {
         | 
| 271 | 
            +
                if ((*it).second.size() < 1) {
         | 
| 272 | 
            +
                  continue;
         | 
| 273 | 
            +
                }
         | 
| 274 | 
            +
                tmp.push_back(
         | 
| 275 | 
            +
            	std::make_tuple((*it).first, DITHER_INT_T, (*it).second.size()));
         | 
| 276 | 
            +
              }
         | 
| 277 | 
            +
             | 
| 278 | 
            +
              for (auto it = str_params_.cbegin(); it != str_params_.cend(); ++it) {
         | 
| 279 | 
            +
                if ((*it).second.size() < 1) {
         | 
| 280 | 
            +
                  continue;
         | 
| 281 | 
            +
                }
         | 
| 282 | 
            +
                tmp.push_back(
         | 
| 283 | 
            +
            	std::make_tuple((*it).first, DITHER_STRING_T, (*it).second.size()));
         | 
| 284 | 
            +
              }
         | 
| 285 | 
            +
             | 
| 286 | 
            +
              for (auto it = bool_params_.cbegin(); it != bool_params_.cend(); ++it) {
         | 
| 287 | 
            +
                if ((*it).second.size() < 1) {
         | 
| 288 | 
            +
                  continue;
         | 
| 289 | 
            +
                }
         | 
| 290 | 
            +
                tmp.push_back(
         | 
| 291 | 
            +
            	std::make_tuple((*it).first, DITHER_BOOL_T, (*it).second.size()));
         | 
| 292 | 
            +
              }
         | 
| 293 | 
            +
             | 
| 294 | 
            +
              std::sort(tmp.begin(), tmp.end(), [](std::tuple<std::string, int, int> a,
         | 
| 295 | 
            +
                    std::tuple<std::string, int, int> b) {
         | 
| 296 | 
            +
                return std::get<2>(a) > std::get<2>(b);
         | 
| 297 | 
            +
              });
         | 
| 298 | 
            +
             | 
| 299 | 
            +
              std::unordered_map<std::string, int> original_name_to_index;
         | 
| 300 | 
            +
              int count = 0;
         | 
| 301 | 
            +
              for(auto it = ordered_param_names_.cbegin(); it != ordered_param_names_.cend(); ++it, count++) {
         | 
| 302 | 
            +
                original_name_to_index.insert({{*it, count}});
         | 
| 303 | 
            +
              }
         | 
| 304 | 
            +
              ordered_param_index_.resize(tmp.size());
         | 
| 305 | 
            +
              reverse_ordered_param_index_.resize(tmp.size());
         | 
| 306 | 
            +
             | 
| 307 | 
            +
              count = 0;
         | 
| 308 | 
            +
              for (auto it = tmp.cbegin(); it != tmp.cend(); ++it, count++) {
         | 
| 309 | 
            +
                std::vector<param> params;
         | 
| 310 | 
            +
                std::vector<dval> dvals;
         | 
| 311 | 
            +
                auto name = std::get<0>(*it);
         | 
| 312 | 
            +
                auto type = std::get<1>(*it);
         | 
| 313 | 
            +
                reverse_param_index_.insert({{count, name}});
         | 
| 314 | 
            +
                ordered_param_index_[count] = original_name_to_index.find(name)->second;
         | 
| 315 | 
            +
                reverse_ordered_param_index_[original_name_to_index.find(name)->second] = count;
         | 
| 316 | 
            +
             | 
| 317 | 
            +
                for (dval i = 0; i < SCHAR_MAX && i < std::get<2>(*it); i++) {
         | 
| 318 | 
            +
                  param my_param;
         | 
| 319 | 
            +
                  my_param.first = count;
         | 
| 320 | 
            +
                  my_param.second = i;
         | 
| 321 | 
            +
                  my_param.name = name;
         | 
| 322 | 
            +
                  my_param.type = type;
         | 
| 323 | 
            +
             | 
| 324 | 
            +
                  params.push_back(my_param);
         | 
| 325 | 
            +
                  dvals.push_back(i);
         | 
| 326 | 
            +
                }
         | 
| 327 | 
            +
                param_cache_.push_back(params);
         | 
| 328 | 
            +
                input_params_.push_back(dvals);
         | 
| 329 | 
            +
              }
         | 
| 330 | 
            +
             | 
| 331 | 
            +
            	std::vector<dval> scratch(param_cache_.size());
         | 
| 332 | 
            +
            	for(auto it = original_previously_tested_.cbegin(); it != original_previously_tested_.cend(); ++it) {
         | 
| 333 | 
            +
            		for(std::size_t i = 0; i < scratch.size(); i++) {
         | 
| 334 | 
            +
            			scratch[reverse_ordered_param_index_[i]] = (*it)[i];
         | 
| 335 | 
            +
            		}
         | 
| 336 | 
            +
            		previously_tested_.push_back(scratch);
         | 
| 337 | 
            +
            	}
         | 
| 338 | 
            +
             | 
| 339 | 
            +
              // transform constraints
         | 
| 340 | 
            +
              for(std::size_t j = 0; j < constraints.size(); j++) {
         | 
| 341 | 
            +
                for(std::size_t i = 0; i < scratch.size(); i++) {
         | 
| 342 | 
            +
                  scratch[reverse_ordered_param_index_[i]] = constraints[j][i];
         | 
| 343 | 
            +
                }
         | 
| 344 | 
            +
                std::copy(scratch.begin(), scratch.end(), constraints[j].begin());
         | 
| 345 | 
            +
              }
         | 
| 346 | 
            +
            }
         | 
| 347 | 
            +
             | 
| 348 | 
            +
            int Ipog::size() {
         | 
| 349 | 
            +
              return solution_size;
         | 
| 350 | 
            +
            }
         | 
| 351 | 
            +
             | 
| 352 | 
            +
            std::string *Ipog::header() {
         | 
| 353 | 
            +
              std::string *result = new std::string[param_cache_.size()];
         | 
| 354 | 
            +
              for (std::size_t i = 0; i < param_cache_.size(); i++) {
         | 
| 355 | 
            +
                result[i] = reverse_param_index_[i];
         | 
| 356 | 
            +
              }
         | 
| 357 | 
            +
              return result;
         | 
| 358 | 
            +
            }
         | 
| 359 | 
            +
             | 
| 360 | 
            +
              void Ipog::add_constraint(const int* constraint, const unsigned int length) {
         | 
| 361 | 
            +
                std::vector<dval> tmp(length);
         | 
| 362 | 
            +
                std::copy(constraint, constraint+length, tmp.begin());
         | 
| 363 | 
            +
                for(auto it = tmp.cbegin(); it != tmp.cend(); ++it) {
         | 
| 364 | 
            +
                  if((*it) > SCHAR_MAX) {
         | 
| 365 | 
            +
                    std::cerr << "WARNING: constraint value > " << SCHAR_MAX << " behavior undefined" << std::endl;
         | 
| 366 | 
            +
                  }
         | 
| 367 | 
            +
                }
         | 
| 368 | 
            +
                constraints.push_back(tmp);
         | 
| 369 | 
            +
              }
         | 
| 370 | 
            +
             | 
| 371 | 
            +
            void Ipog::ground_solutions() {
         | 
| 372 | 
            +
            	std::size_t solution_count = 0;
         | 
| 373 | 
            +
              std::vector<dval> transform_scratch(param_cache_.size(), 0);
         | 
| 374 | 
            +
             | 
| 375 | 
            +
              auto prev = bound_.cbefore_begin();
         | 
| 376 | 
            +
              auto next = bound_.begin();
         | 
| 377 | 
            +
              auto end = bound_.cend();
         | 
| 378 | 
            +
              while (next != end) {
         | 
| 379 | 
            +
                if (constraint_handler->violate_constraints(*next) || has_previously_tested(*next)) {
         | 
| 380 | 
            +
                  next = bound_.erase_after(prev);
         | 
| 381 | 
            +
                } else {
         | 
| 382 | 
            +
                  transform(transform_scratch, *next);
         | 
| 383 | 
            +
            			solution_count++;
         | 
| 384 | 
            +
                  ++prev;
         | 
| 385 | 
            +
                  ++next;
         | 
| 386 | 
            +
                }
         | 
| 387 | 
            +
              }
         | 
| 388 | 
            +
             | 
| 389 | 
            +
              while(!unbound_.empty()) {
         | 
| 390 | 
            +
                dtest_case first = unbound_.front();
         | 
| 391 | 
            +
                if(constraint_handler->ground(first)) {
         | 
| 392 | 
            +
                  if(!has_previously_tested(first)) {
         | 
| 393 | 
            +
                    transform(transform_scratch, first);
         | 
| 394 | 
            +
                    solution_count++;
         | 
| 395 | 
            +
                    bound_.push_front(first);
         | 
| 396 | 
            +
                  }
         | 
| 397 | 
            +
                }
         | 
| 398 | 
            +
                unbound_.pop_front();
         | 
| 399 | 
            +
              }
         | 
| 400 | 
            +
            	solution_size = solution_count;
         | 
| 401 | 
            +
              }
         | 
| 402 | 
            +
             | 
| 403 | 
            +
            void Ipog::fill(int *solution) {
         | 
| 404 | 
            +
              std::size_t i = 0;
         | 
| 405 | 
            +
              for(auto it = bound_.cbegin(); it != bound_.cend(); ++it) {
         | 
| 406 | 
            +
                for(auto iit = (*it).cbegin(); iit != (*it).cend(); ++iit, i++) {
         | 
| 407 | 
            +
                  solution[i] = *iit;
         | 
| 408 | 
            +
                }
         | 
| 409 | 
            +
              }
         | 
| 410 | 
            +
            }
         | 
| 411 | 
            +
             | 
| 412 | 
            +
            void Ipog::add_previously_tested(const int values[], const std::size_t length) {
         | 
| 413 | 
            +
            	std::vector<dval> tmp(length);
         | 
| 414 | 
            +
            	std::copy(values, values+length, tmp.begin());
         | 
| 415 | 
            +
            	original_previously_tested_.push_back(tmp);
         | 
| 416 | 
            +
            }
         | 
| 417 | 
            +
             | 
| 418 | 
            +
              inline bool Ipog::has_previously_tested(std::vector<param>& test_case) {
         | 
| 419 | 
            +
                for(auto it = previously_tested_.cbegin(); it != previously_tested_.cend(); ++it) {
         | 
| 420 | 
            +
                  bool flag = true;
         | 
| 421 | 
            +
                  for(auto iit = test_case.cbegin(); iit != test_case.cend(); ++iit) {
         | 
| 422 | 
            +
                    if((*it)[(*iit).first] != (*iit).second) {
         | 
| 423 | 
            +
                      flag = false;
         | 
| 424 | 
            +
                      break;
         | 
| 425 | 
            +
                    }
         | 
| 426 | 
            +
                  }
         | 
| 427 | 
            +
                  if(flag) {
         | 
| 428 | 
            +
                    return true;
         | 
| 429 | 
            +
                  }
         | 
| 430 | 
            +
                }
         | 
| 431 | 
            +
                return false;
         | 
| 432 | 
            +
              }
         | 
| 433 | 
            +
             | 
| 434 | 
            +
              inline bool Ipog::has_previously_tested(dtest_case& test_case) {
         | 
| 435 | 
            +
                for(auto it = previously_tested_.cbegin(); it != previously_tested_.cend(); ++it) {
         | 
| 436 | 
            +
                  if(std::equal(test_case.cbegin(), test_case.cend(), (*it).cbegin())) {
         | 
| 437 | 
            +
                    return true;
         | 
| 438 | 
            +
                  }
         | 
| 439 | 
            +
                }
         | 
| 440 | 
            +
                return false;
         | 
| 441 | 
            +
              }
         | 
| 442 | 
            +
             | 
| 443 | 
            +
              inline bool Ipog::has_previously_tested(const int k, dtest_case& test_case) {
         | 
| 444 | 
            +
                for(auto it = previously_tested_.cbegin(); it != previously_tested_.cend(); ++it) {
         | 
| 445 | 
            +
                  if(std::equal(test_case.cbegin(), test_case.cbegin()+k, (*it).cbegin())) {
         | 
| 446 | 
            +
                    return true;
         | 
| 447 | 
            +
                  }
         | 
| 448 | 
            +
                }
         | 
| 449 | 
            +
                return false;
         | 
| 450 | 
            +
              }
         | 
| 451 | 
            +
            }
         | 
    
        data/ext/dither/ipog.h
    ADDED
    
    | @@ -0,0 +1,128 @@ | |
| 1 | 
            +
            /*
         | 
| 2 | 
            +
             *
         | 
| 3 | 
            +
             * Copyright (C) 2015 Jason Gowan
         | 
| 4 | 
            +
             * All rights reserved.
         | 
| 5 | 
            +
             *
         | 
| 6 | 
            +
             * This software may be modified and distributed under the terms
         | 
| 7 | 
            +
             * of the BSD license.  See the LICENSE file for details.
         | 
| 8 | 
            +
             */
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            #ifndef IPOG_H_
         | 
| 11 | 
            +
            #define IPOG_H_
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            #include <vector>
         | 
| 14 | 
            +
            #include <iostream>
         | 
| 15 | 
            +
            #include <forward_list>
         | 
| 16 | 
            +
            #include <algorithm>
         | 
| 17 | 
            +
            #include <string>
         | 
| 18 | 
            +
            #include <unordered_map>
         | 
| 19 | 
            +
            #include "combinations.h"
         | 
| 20 | 
            +
            #include "dither_types.h"
         | 
| 21 | 
            +
            #include "base_constraint_handler.h"
         | 
| 22 | 
            +
            #include "simple_constraint_handler.h"
         | 
| 23 | 
            +
             | 
| 24 | 
            +
            namespace dither {
         | 
| 25 | 
            +
             | 
| 26 | 
            +
            class Ipog {
         | 
| 27 | 
            +
              std::size_t t_;
         | 
| 28 | 
            +
              std::vector<std::vector<dval>> input_params_;
         | 
| 29 | 
            +
              std::vector<std::vector<param>> param_cache_;
         | 
| 30 | 
            +
              std::forward_list<dtest_case> bound_;
         | 
| 31 | 
            +
              std::forward_list<dtest_case> unbound_;
         | 
| 32 | 
            +
              std::unordered_map<std::string, std::vector<int>> int_params_;
         | 
| 33 | 
            +
              std::unordered_map<std::string, std::vector<std::string>> str_params_;
         | 
| 34 | 
            +
              std::unordered_map<std::string, std::vector<bool>> bool_params_;
         | 
| 35 | 
            +
              std::vector<std::string> ordered_param_names_;
         | 
| 36 | 
            +
              std::vector<int> ordered_param_index_;
         | 
| 37 | 
            +
              std::vector<int> reverse_ordered_param_index_;
         | 
| 38 | 
            +
              std::unordered_map<std::string, int> param_index_;
         | 
| 39 | 
            +
              std::unordered_map<int, std::string> reverse_param_index_;
         | 
| 40 | 
            +
              dtest_case merge_scratch_;
         | 
| 41 | 
            +
              BaseConstraintHandler* constraint_handler;
         | 
| 42 | 
            +
              std::vector<std::vector<dval>> constraints;
         | 
| 43 | 
            +
              std::vector<dval> ranges;
         | 
| 44 | 
            +
            	std::size_t solution_size;
         | 
| 45 | 
            +
            	std::vector<std::vector<dval>> original_previously_tested_;
         | 
| 46 | 
            +
            	std::vector<std::vector<dval>> previously_tested_;
         | 
| 47 | 
            +
             | 
| 48 | 
            +
              inline void transform(std::vector<dval>& scratch, std::vector<dval>& test_case) {
         | 
| 49 | 
            +
                for(std::size_t i = 0; i < test_case.size(); i++) {
         | 
| 50 | 
            +
                  scratch[ordered_param_index_[i]] = test_case[i];
         | 
| 51 | 
            +
                }
         | 
| 52 | 
            +
                std::copy(scratch.cbegin(), scratch.cend(), test_case.begin());
         | 
| 53 | 
            +
              }
         | 
| 54 | 
            +
             | 
| 55 | 
            +
              inline bool has_previously_tested(std::vector<param>& test_case);
         | 
| 56 | 
            +
              inline bool has_previously_tested(dtest_case& test_case);
         | 
| 57 | 
            +
              inline bool has_previously_tested(const int, dtest_case& test_case);
         | 
| 58 | 
            +
             | 
| 59 | 
            +
             public:
         | 
| 60 | 
            +
              Ipog();
         | 
| 61 | 
            +
              ~Ipog();
         | 
| 62 | 
            +
              Ipog(const unsigned int);
         | 
| 63 | 
            +
              void set_t(const unsigned int t) { t_ = t; }
         | 
| 64 | 
            +
              void add_parameter(const std::string, const int[], const unsigned int);
         | 
| 65 | 
            +
              void add_parameter(const std::string, const std::string[], const unsigned int);
         | 
| 66 | 
            +
              void add_parameter(const std::string);
         | 
| 67 | 
            +
              void init_bound();
         | 
| 68 | 
            +
              void init_param_cache();
         | 
| 69 | 
            +
              void run();
         | 
| 70 | 
            +
              int size();
         | 
| 71 | 
            +
              std::string *header();
         | 
| 72 | 
            +
              void ground_solutions();
         | 
| 73 | 
            +
              inline bool is_valid() { return t_ <= param_cache_.size(); }
         | 
| 74 | 
            +
              std::forward_list<std::vector<param>> cover(int);
         | 
| 75 | 
            +
              const int maximize_coverage(const int, dtest_case &,
         | 
| 76 | 
            +
                                          std::forward_list<std::vector<param>> &);
         | 
| 77 | 
            +
              void add_constraint(const int[], const unsigned int);
         | 
| 78 | 
            +
            	void add_previously_tested(const int[], const std::size_t);
         | 
| 79 | 
            +
              inline bool is_covered(const dtest_case &test_case,
         | 
| 80 | 
            +
                                     const std::vector<param> ¶ms);
         | 
| 81 | 
            +
              inline bool is_covered(const std::vector<param> ¶ms);
         | 
| 82 | 
            +
              inline const int merge(const int, dtest_case &, const std::vector<param> &);
         | 
| 83 | 
            +
              void display_raw_solution();
         | 
| 84 | 
            +
              void fill(int[]);
         | 
| 85 | 
            +
              inline void display_header() {
         | 
| 86 | 
            +
                for (std::size_t i = 0; i < param_cache_.size();) {
         | 
| 87 | 
            +
                  std::cout << reverse_param_index_[i];
         | 
| 88 | 
            +
                  if (++i < param_cache_.size()) {
         | 
| 89 | 
            +
                    std::cout << ',';
         | 
| 90 | 
            +
                  }
         | 
| 91 | 
            +
                }
         | 
| 92 | 
            +
                std::cout << std::endl;
         | 
| 93 | 
            +
              }
         | 
| 94 | 
            +
             | 
| 95 | 
            +
              inline void display_test_case(const dtest_case &test_case) {
         | 
| 96 | 
            +
                for (std::size_t i = 0; i < test_case.size();) {
         | 
| 97 | 
            +
                  const dval value = test_case[i];
         | 
| 98 | 
            +
                  if (value == -1) {
         | 
| 99 | 
            +
                    std::cout << '-';
         | 
| 100 | 
            +
                    if (++i < test_case.size()) {
         | 
| 101 | 
            +
                      std::cout << ',';
         | 
| 102 | 
            +
                    }
         | 
| 103 | 
            +
                    continue;
         | 
| 104 | 
            +
                  }
         | 
| 105 | 
            +
                  const param my_param = param_cache_[reverse_ordered_param_index_[i]][value];
         | 
| 106 | 
            +
                  switch (my_param.type) {
         | 
| 107 | 
            +
                    case DITHER_INT_T:
         | 
| 108 | 
            +
                      std::cout << int_params_[my_param.name][my_param.second];
         | 
| 109 | 
            +
                      break;
         | 
| 110 | 
            +
                    case DITHER_BOOL_T:
         | 
| 111 | 
            +
                      std::cout << std::boolalpha
         | 
| 112 | 
            +
                                << bool_params_[my_param.name][my_param.second];
         | 
| 113 | 
            +
                      break;
         | 
| 114 | 
            +
                    case DITHER_STRING_T:
         | 
| 115 | 
            +
                      std::cout << str_params_[my_param.name][my_param.second];
         | 
| 116 | 
            +
                      break;
         | 
| 117 | 
            +
                  }
         | 
| 118 | 
            +
                  if (++i < test_case.size()) {
         | 
| 119 | 
            +
                    std::cout << ',';
         | 
| 120 | 
            +
                  }
         | 
| 121 | 
            +
                }
         | 
| 122 | 
            +
                std::cout << std::endl;
         | 
| 123 | 
            +
              }
         | 
| 124 | 
            +
            };
         | 
| 125 | 
            +
             | 
| 126 | 
            +
            }
         | 
| 127 | 
            +
             | 
| 128 | 
            +
            #endif
         | 
| @@ -0,0 +1,119 @@ | |
| 1 | 
            +
            /*
         | 
| 2 | 
            +
             *
         | 
| 3 | 
            +
             * Copyright (C) 2015 Jason Gowan
         | 
| 4 | 
            +
             * All rights reserved.
         | 
| 5 | 
            +
             *
         | 
| 6 | 
            +
             * This software may be modified and distributed under the terms
         | 
| 7 | 
            +
             * of the BSD license.  See the LICENSE file for details.
         | 
| 8 | 
            +
             */
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            #include "simple_constraint_handler.h"
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            namespace dither {
         | 
| 13 | 
            +
             | 
| 14 | 
            +
              SimpleConstraintHandler::SimpleConstraintHandler(std::vector<dval>& ranges, std::vector<std::vector<dval>>& pconstraints) : params(ranges) {
         | 
| 15 | 
            +
                for(auto it = pconstraints.cbegin(); it != pconstraints.cend(); ++it) {
         | 
| 16 | 
            +
                  std::vector<std::pair<std::size_t, dval>> constraint;
         | 
| 17 | 
            +
                  int i = 0;
         | 
| 18 | 
            +
                  for(auto iit = (*it).cbegin(); iit != (*it).cend(); ++iit, i++) {
         | 
| 19 | 
            +
                    if((*iit) != -1) {
         | 
| 20 | 
            +
                      constraint.push_back(std::make_pair(i, *iit));
         | 
| 21 | 
            +
                    }
         | 
| 22 | 
            +
                  }
         | 
| 23 | 
            +
                  constraints.push_back(constraint);
         | 
| 24 | 
            +
                }
         | 
| 25 | 
            +
                std::sort(constraints.begin(), constraints.end(), [](std::vector<std::pair<std::size_t, dval>>& a, std::vector<std::pair<std::size_t, dval>>& b) { return a.size() < b.size(); });
         | 
| 26 | 
            +
              }
         | 
| 27 | 
            +
             | 
| 28 | 
            +
              bool SimpleConstraintHandler::violate_constraints(const dtest_case &test_case) {
         | 
| 29 | 
            +
                for(auto constraint = constraints.cbegin(); constraint != constraints.cend(); ++constraint) {
         | 
| 30 | 
            +
                  if(violate_constraint(test_case, *constraint)) {
         | 
| 31 | 
            +
                    return true;
         | 
| 32 | 
            +
                  }
         | 
| 33 | 
            +
                }
         | 
| 34 | 
            +
                return false;
         | 
| 35 | 
            +
              }
         | 
| 36 | 
            +
             | 
| 37 | 
            +
              inline bool SimpleConstraintHandler::violate_constraint(const dtest_case& test_case, const std::vector<std::pair<std::size_t, dval>>& constraint) {
         | 
| 38 | 
            +
                for(auto it = constraint.cbegin(); it != constraint.cend(); ++it) {
         | 
| 39 | 
            +
                  auto value = test_case[(*it).first];
         | 
| 40 | 
            +
                  if(value == -1 || value != (*it).second) {
         | 
| 41 | 
            +
                    return false;
         | 
| 42 | 
            +
                  }
         | 
| 43 | 
            +
                }
         | 
| 44 | 
            +
                return true;
         | 
| 45 | 
            +
              }
         | 
| 46 | 
            +
             | 
| 47 | 
            +
              bool SimpleConstraintHandler::violate_constraints(const std::vector<param> &test_case) {
         | 
| 48 | 
            +
                for(auto constraint = constraints.cbegin(); constraint != constraints.cend(); ++constraint) {
         | 
| 49 | 
            +
                  if(violate_constraint(test_case, *constraint)) {
         | 
| 50 | 
            +
                    return true;
         | 
| 51 | 
            +
                  }
         | 
| 52 | 
            +
                }
         | 
| 53 | 
            +
                return false;
         | 
| 54 | 
            +
              }
         | 
| 55 | 
            +
             | 
| 56 | 
            +
              inline bool SimpleConstraintHandler::violate_constraint(const std::vector<param>& test_case, const std::vector<std::pair<std::size_t, dval>>& constraint) {
         | 
| 57 | 
            +
                if(test_case.size() < constraint.size()) {
         | 
| 58 | 
            +
                  return false;
         | 
| 59 | 
            +
                }
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                std::size_t count = 0;
         | 
| 62 | 
            +
                for(auto it = constraint.cbegin(); it != constraint.cend(); ++it) {
         | 
| 63 | 
            +
                  for(auto iit = test_case.cbegin(); iit != test_case.cend(); ++iit) {
         | 
| 64 | 
            +
                    if((*iit).first == (*it).first && (*iit).second == (*it).second) {
         | 
| 65 | 
            +
                      count++;
         | 
| 66 | 
            +
                      break;
         | 
| 67 | 
            +
                    }
         | 
| 68 | 
            +
                  }
         | 
| 69 | 
            +
                }
         | 
| 70 | 
            +
                if(count == constraint.size()) {
         | 
| 71 | 
            +
                  return true;
         | 
| 72 | 
            +
                }
         | 
| 73 | 
            +
                return false;
         | 
| 74 | 
            +
              }
         | 
| 75 | 
            +
             | 
| 76 | 
            +
              bool SimpleConstraintHandler::ground(dtest_case &test_case) {
         | 
| 77 | 
            +
                std::vector<std::size_t> indexes;
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                // find unbound indexes
         | 
| 80 | 
            +
                std::size_t i = 0;
         | 
| 81 | 
            +
                for (auto it = test_case.begin(); it != test_case.end(); ++it, i++) {
         | 
| 82 | 
            +
                  if ((*it) == -1) {
         | 
| 83 | 
            +
                    indexes.push_back(i);
         | 
| 84 | 
            +
                  }
         | 
| 85 | 
            +
                }
         | 
| 86 | 
            +
                if(indexes.size() == 0) {
         | 
| 87 | 
            +
                  return true;
         | 
| 88 | 
            +
                }
         | 
| 89 | 
            +
                std::vector<dval> bound_values(indexes.size(), 0);
         | 
| 90 | 
            +
                i = 0;
         | 
| 91 | 
            +
             | 
| 92 | 
            +
            LOOP:{
         | 
| 93 | 
            +
                   do {
         | 
| 94 | 
            +
                     test_case[indexes[i]] = bound_values[i];
         | 
| 95 | 
            +
                     if(violate_constraints(test_case)) {
         | 
| 96 | 
            +
                       // try to bump a bound value... return false if no valid value
         | 
| 97 | 
            +
                       const std::size_t upper_bound = i + 1;
         | 
| 98 | 
            +
                       for(std::size_t j = 0; j < upper_bound; j++) {
         | 
| 99 | 
            +
                         auto bump_value = bound_values[i] + 1;
         | 
| 100 | 
            +
                         if(bump_value < params[indexes[i]]) {
         | 
| 101 | 
            +
                           bound_values[i] = bump_value;
         | 
| 102 | 
            +
                           goto LOOP;
         | 
| 103 | 
            +
                         }
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                         // unwind
         | 
| 106 | 
            +
                         bound_values[i] = 0;
         | 
| 107 | 
            +
                         test_case[indexes[i]] = -1;
         | 
| 108 | 
            +
                         i--;
         | 
| 109 | 
            +
                       }
         | 
| 110 | 
            +
             | 
| 111 | 
            +
                       return false;
         | 
| 112 | 
            +
                     }
         | 
| 113 | 
            +
                     i++;
         | 
| 114 | 
            +
                   }while(i < indexes.size());
         | 
| 115 | 
            +
                 }
         | 
| 116 | 
            +
             | 
| 117 | 
            +
                 return true;
         | 
| 118 | 
            +
              }
         | 
| 119 | 
            +
            }
         |