dep_selector 0.0.1
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.
- data/ext/dep_gecode/dep_selector_swig.i +64 -0
- data/ext/dep_gecode/dep_selector_swig_wrap.cxx +2462 -0
- data/ext/dep_gecode/dep_selector_to_gecode.cpp +557 -0
- data/ext/dep_gecode/dep_selector_to_gecode.h +136 -0
- data/ext/dep_gecode/dep_selector_to_gecode_interface.cpp +122 -0
- data/ext/dep_gecode/dep_selector_to_gecode_interface.h +70 -0
- data/ext/dep_gecode/extconf.rb +36 -0
- data/ext/dep_gecode/lib/dep_selector_to_gecode.rb +11 -0
- data/lib/dep_selector.rb +32 -0
- data/lib/dep_selector/densely_packed_set.rb +59 -0
- data/lib/dep_selector/dependency.rb +46 -0
- data/lib/dep_selector/dependency_graph.rb +83 -0
- data/lib/dep_selector/error_reporter.rb +28 -0
- data/lib/dep_selector/error_reporter/simple_tree_traverser.rb +183 -0
- data/lib/dep_selector/exceptions.rb +67 -0
- data/lib/dep_selector/gecode_wrapper.rb +151 -0
- data/lib/dep_selector/package.rb +116 -0
- data/lib/dep_selector/package_version.rb +68 -0
- data/lib/dep_selector/selector.rb +264 -0
- data/lib/dep_selector/solution_constraint.rb +22 -0
- data/lib/dep_selector/version.rb +74 -0
- data/lib/dep_selector/version_constraint.rb +120 -0
- metadata +81 -0
@@ -0,0 +1,557 @@
|
|
1
|
+
//
|
2
|
+
// Author:: Christopher Walters (<cw@opscode.com>)
|
3
|
+
// Author:: Mark Anderson (<mark@opscode.com>)
|
4
|
+
// Copyright:: Copyright (c) 2010-2011 Opscode, Inc.
|
5
|
+
// License:: Apache License, Version 2.0
|
6
|
+
//
|
7
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
// you may not use this file except in compliance with the License.
|
9
|
+
// You may obtain a copy of the License at
|
10
|
+
//
|
11
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
//
|
13
|
+
// Unless required by applicable law or agreed to in writing, software
|
14
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
// See the License for the specific language governing permissions and
|
17
|
+
// limitations under the License.
|
18
|
+
//
|
19
|
+
|
20
|
+
#include <gecode/driver.hh>
|
21
|
+
#include <gecode/int.hh>
|
22
|
+
#include <gecode/minimodel.hh>
|
23
|
+
#include <gecode/gist.hh>
|
24
|
+
#include <gecode/search.hh>
|
25
|
+
|
26
|
+
#include "dep_selector_to_gecode.h"
|
27
|
+
|
28
|
+
#include <limits>
|
29
|
+
#include <iostream>
|
30
|
+
#include <vector>
|
31
|
+
|
32
|
+
//#define DEBUG
|
33
|
+
//#define USE_DUMB_BRANCHING
|
34
|
+
#define VECTOR_CONSTRAIN
|
35
|
+
|
36
|
+
using namespace Gecode;
|
37
|
+
const int VersionProblem::UNRESOLVED_VARIABLE = INT_MIN;
|
38
|
+
const int VersionProblem::MIN_TRUST_LEVEL = 0;
|
39
|
+
const int VersionProblem::MAX_TRUST_LEVEL = 10;
|
40
|
+
const int VersionProblem::MAX_PREFERRED_WEIGHT = 10;
|
41
|
+
|
42
|
+
VersionProblem::VersionProblem(int packageCount)
|
43
|
+
: size(packageCount), finalized(false), cur_package(0), package_versions(*this, packageCount),
|
44
|
+
disabled_package_variables(*this, packageCount, 0, 1), total_disabled(*this, 0, packageCount*MAX_TRUST_LEVEL),
|
45
|
+
total_required_disabled(*this, 0, packageCount), total_induced_disabled(*this, 0, packageCount),
|
46
|
+
total_suspicious_disabled(*this, 0, packageCount),
|
47
|
+
is_required(new int[packageCount]),
|
48
|
+
is_suspicious(new int[packageCount]),
|
49
|
+
at_latest(*this, packageCount, 0, 1),
|
50
|
+
// These domains could be narrowed a bit; check later
|
51
|
+
total_preferred_at_latest(*this, -packageCount*MAX_PREFERRED_WEIGHT, packageCount*MAX_PREFERRED_WEIGHT),
|
52
|
+
total_not_preferred_at_latest(*this, -packageCount, packageCount),
|
53
|
+
preferred_at_latest_weights(new int[packageCount])
|
54
|
+
{
|
55
|
+
for (int i = 0; i < packageCount; i++)
|
56
|
+
{
|
57
|
+
preferred_at_latest_weights[i] = 0;
|
58
|
+
is_required[i] = 0;
|
59
|
+
is_suspicious[i] = 0;
|
60
|
+
}
|
61
|
+
}
|
62
|
+
|
63
|
+
VersionProblem::VersionProblem(bool share, VersionProblem & s)
|
64
|
+
: Space(share, s), size(s.size),
|
65
|
+
finalized(s.finalized), cur_package(s.cur_package),
|
66
|
+
disabled_package_variables(s.disabled_package_variables), total_disabled(s.total_disabled),
|
67
|
+
total_required_disabled(s.total_required_disabled), total_induced_disabled(s.total_induced_disabled),
|
68
|
+
total_suspicious_disabled(s.total_suspicious_disabled),
|
69
|
+
is_required(NULL), is_suspicious(NULL),
|
70
|
+
at_latest(s.at_latest),
|
71
|
+
total_preferred_at_latest(s.total_preferred_at_latest),
|
72
|
+
total_not_preferred_at_latest(s.total_preferred_at_latest),
|
73
|
+
preferred_at_latest_weights(NULL)
|
74
|
+
{
|
75
|
+
package_versions.update(*this, share, s.package_versions);
|
76
|
+
disabled_package_variables.update(*this, share, s.disabled_package_variables);
|
77
|
+
total_disabled.update(*this, share, s.total_disabled);
|
78
|
+
total_required_disabled.update(*this, share, s.total_required_disabled);
|
79
|
+
total_induced_disabled.update(*this, share, s.total_induced_disabled);
|
80
|
+
total_suspicious_disabled.update(*this, share, s.total_suspicious_disabled);
|
81
|
+
at_latest.update(*this, share, s.at_latest);
|
82
|
+
total_preferred_at_latest.update(*this, share, s.total_preferred_at_latest);
|
83
|
+
total_not_preferred_at_latest.update(*this, share, s.total_not_preferred_at_latest);
|
84
|
+
}
|
85
|
+
|
86
|
+
// Support for gecode
|
87
|
+
Space* VersionProblem::copy(bool share)
|
88
|
+
{
|
89
|
+
return new VersionProblem(share,*this);
|
90
|
+
}
|
91
|
+
|
92
|
+
VersionProblem::~VersionProblem()
|
93
|
+
{
|
94
|
+
delete[] preferred_at_latest_weights;
|
95
|
+
delete[] is_required;
|
96
|
+
delete[] is_suspicious;
|
97
|
+
}
|
98
|
+
|
99
|
+
int VersionProblem::Size()
|
100
|
+
{
|
101
|
+
return size;
|
102
|
+
}
|
103
|
+
|
104
|
+
int VersionProblem::PackageCount()
|
105
|
+
{
|
106
|
+
return cur_package;
|
107
|
+
}
|
108
|
+
|
109
|
+
int
|
110
|
+
VersionProblem::AddPackage(int minVersion, int maxVersion, int currentVersion)
|
111
|
+
{
|
112
|
+
if (cur_package == size) {
|
113
|
+
return -1;
|
114
|
+
}
|
115
|
+
|
116
|
+
#ifdef DEBUG
|
117
|
+
std::cout << "Adding package id " << cur_package << '/' << size << ": min = " << minVersion << ", max = " << maxVersion << ", current version " << currentVersion << std::endl;
|
118
|
+
std::cout.flush();
|
119
|
+
#endif // DEBUG
|
120
|
+
int index = cur_package;
|
121
|
+
cur_package++;
|
122
|
+
// IntVar version(*this, minVersion, maxVersion);
|
123
|
+
package_versions[index] = IntVar(*this, minVersion, maxVersion);
|
124
|
+
|
125
|
+
// register the binding of package to version that corresponds to the package's latest
|
126
|
+
rel(*this, package_versions[index], IRT_EQ, maxVersion, at_latest[index]);
|
127
|
+
|
128
|
+
return index;
|
129
|
+
}
|
130
|
+
|
131
|
+
bool
|
132
|
+
VersionProblem::AddVersionConstraint(int packageId, int version,
|
133
|
+
int dependentPackageId, int minDependentVersion, int maxDependentVersion)
|
134
|
+
{
|
135
|
+
BoolVar version_match(*this, 0, 1);
|
136
|
+
BoolVar depend_match(*this, 0, 1);
|
137
|
+
BoolVar predicated_depend_match(*this, 0, 1);
|
138
|
+
|
139
|
+
#ifdef DEBUG
|
140
|
+
std::cout << "Add VC for " << packageId << " @ " << version << " depPkg " << dependentPackageId;
|
141
|
+
std::cout << " [ " << minDependentVersion << ", " << maxDependentVersion << " ]" << std::endl;
|
142
|
+
std::cout.flush();
|
143
|
+
#endif // DEBUG
|
144
|
+
|
145
|
+
|
146
|
+
//version_flags << version_match;
|
147
|
+
// Constrain pred to reify package @ version
|
148
|
+
rel(*this, package_versions[packageId], IRT_EQ, version, version_match);
|
149
|
+
// Add the predicated version constraints imposed on dependent package
|
150
|
+
|
151
|
+
// package_versions[dependendPackageId] in domain [minDependentVersion,maxDependentVersion] <=> depend_match
|
152
|
+
dom(*this, package_versions[dependentPackageId], minDependentVersion, maxDependentVersion, depend_match);
|
153
|
+
|
154
|
+
// disabled_package_variables[dependentPackageId] OR depend_match <=> predicated_depend_match
|
155
|
+
// rel(*this, disabled_package_variables[dependentPackageId], BOT_OR, depend_match, version_match);
|
156
|
+
|
157
|
+
rel(*this, disabled_package_variables[dependentPackageId], BOT_OR, depend_match, predicated_depend_match);
|
158
|
+
rel(*this, version_match, BOT_IMP, predicated_depend_match, 1);
|
159
|
+
}
|
160
|
+
|
161
|
+
void
|
162
|
+
VersionProblem::MarkPackageSuspicious(int packageId)
|
163
|
+
{
|
164
|
+
is_suspicious[packageId] = 1;
|
165
|
+
}
|
166
|
+
|
167
|
+
void
|
168
|
+
VersionProblem::MarkPackageRequired(int packageId)
|
169
|
+
{
|
170
|
+
is_required[packageId] = 1;
|
171
|
+
}
|
172
|
+
|
173
|
+
void
|
174
|
+
VersionProblem::MarkPackagePreferredToBeAtLatest(int packageId, int weight)
|
175
|
+
{
|
176
|
+
preferred_at_latest_weights[packageId] = std::max(MAX_PREFERRED_WEIGHT, std::min(0, weight));
|
177
|
+
}
|
178
|
+
|
179
|
+
void VersionProblem::Finalize()
|
180
|
+
{
|
181
|
+
#ifdef DEBUG
|
182
|
+
std::cout << "Finalization Started" << std::endl;
|
183
|
+
std::cout.flush();
|
184
|
+
#endif // DEBUG
|
185
|
+
finalized = true;
|
186
|
+
|
187
|
+
// Setup constraint for cost
|
188
|
+
// We wish to minimize the total number of disabled packages, by priority ranks
|
189
|
+
IntArgs disabled_required_weights(size, is_required);
|
190
|
+
linear(*this, disabled_required_weights, disabled_package_variables, IRT_EQ, total_required_disabled);
|
191
|
+
#ifdef DEBUG
|
192
|
+
std::cout << "disabled_required_weights: " << disabled_required_weights << std::endl;
|
193
|
+
std::cout << "total_required_disabled: " << total_required_disabled << std::endl;
|
194
|
+
#endif // DEBUG
|
195
|
+
|
196
|
+
IntArgs disabled_induced_weights(size);
|
197
|
+
for (int i = 0; i < size; i++) {
|
198
|
+
disabled_induced_weights[i] = !(is_required[i] || is_suspicious[i]);
|
199
|
+
}
|
200
|
+
linear(*this, disabled_induced_weights, disabled_package_variables, IRT_EQ, total_induced_disabled);
|
201
|
+
#ifdef DEBUG
|
202
|
+
std::cout << "disabled_induced_weights: " << disabled_induced_weights << std::endl;
|
203
|
+
std::cout << "total_induced_disabled: " << total_induced_disabled << std::endl;
|
204
|
+
#endif // DEBUG
|
205
|
+
|
206
|
+
IntArgs disabled_suspicious_weights(size, is_suspicious);
|
207
|
+
linear(*this, disabled_suspicious_weights, disabled_package_variables, IRT_EQ, total_suspicious_disabled);
|
208
|
+
#ifdef DEBUG
|
209
|
+
std::cout << "disabled_suspicious_weights: " << disabled_suspicious_weights << std::endl;
|
210
|
+
std::cout << "total_suspicious_disabled: " << total_suspicious_disabled << std::endl;
|
211
|
+
#endif // DEBUG
|
212
|
+
|
213
|
+
linear(*this, disabled_package_variables, IRT_EQ, total_disabled);
|
214
|
+
#ifdef DEBUG
|
215
|
+
std::cout << "total_disabled: " << total_disabled << std::endl;
|
216
|
+
#endif DEBUG
|
217
|
+
|
218
|
+
// Setup computation for total_preferred_at_latest
|
219
|
+
// We wish to maximize the total number of packages at their latest versions in the preferred tier of packages
|
220
|
+
// We negate the weights in the cost function to make it fit into the context of a minimization problem.
|
221
|
+
for (int i = 0; i < size; i++) {
|
222
|
+
preferred_at_latest_weights[i] = -preferred_at_latest_weights[i];
|
223
|
+
}
|
224
|
+
IntArgs preferred_at_latest_weights_args(size, preferred_at_latest_weights);
|
225
|
+
linear(*this, preferred_at_latest_weights_args, at_latest, IRT_EQ, total_preferred_at_latest);
|
226
|
+
#ifdef DEBUG
|
227
|
+
std::cout << "preferred_at_latest_weights_args: " << preferred_at_latest_weights_args << std::endl;
|
228
|
+
std::cout << "total_preferred_at_latest: " << total_preferred_at_latest << std::endl;
|
229
|
+
#endif DEBUG
|
230
|
+
|
231
|
+
// Setup computation for remaining variables
|
232
|
+
// We wish to maximize the total number of packages at their latest version in the non-preferred tier of packages
|
233
|
+
// We negate the weights in the cost function to make it fit into the context of a minimization problem.
|
234
|
+
IntArgs not_preferred_at_latest_weights_args = IntArgs::create(size, 0, 0);
|
235
|
+
for (int i = 0; i < size; i++) {
|
236
|
+
if (preferred_at_latest_weights[i] == 0) {
|
237
|
+
not_preferred_at_latest_weights_args[i] = -1;
|
238
|
+
}
|
239
|
+
}
|
240
|
+
linear(*this, not_preferred_at_latest_weights_args, at_latest, IRT_EQ, total_not_preferred_at_latest);
|
241
|
+
#ifdef DEBUG
|
242
|
+
std::cout << "not_preferred_at_latest_weights_args: " << not_preferred_at_latest_weights_args << std::endl;
|
243
|
+
std::cout << "total_not_preferred_at_latest: " << total_not_preferred_at_latest << std::endl;
|
244
|
+
#endif DEBUG
|
245
|
+
|
246
|
+
// Cleanup
|
247
|
+
// Assign a dummy variable to elements greater than actually used.
|
248
|
+
for (int i = cur_package; i < size; i++) {
|
249
|
+
package_versions[i] = IntVar(*this, -1, -1);
|
250
|
+
disabled_package_variables[i] = BoolVar(*this, 1, 1);
|
251
|
+
}
|
252
|
+
|
253
|
+
#ifdef USE_DUMB_BRANCHING
|
254
|
+
# ifdef DEBUG
|
255
|
+
std::cout << "Adding branching (POOR)" << std::endl;
|
256
|
+
std::cout.flush();
|
257
|
+
# endif // DEBUG
|
258
|
+
// This branching starts as far as possible from the solution, in order to exercise the optimization functions.
|
259
|
+
branch(*this, disabled_package_variables, INT_VAR_SIZE_MIN, INT_VAL_MAX);
|
260
|
+
branch(*this, package_versions, INT_VAR_SIZE_MIN, INT_VAL_MIN);
|
261
|
+
branch(*this, total_required_disabled, INT_VAL_MAX);
|
262
|
+
branch(*this, total_induced_disabled, INT_VAL_MAX);
|
263
|
+
branch(*this, total_suspicious_disabled, INT_VAL_MAX);
|
264
|
+
branch(*this, total_disabled, INT_VAL_MAX);
|
265
|
+
branch(*this, at_latest, INT_VAR_SIZE_MIN, INT_VAL_MIN);
|
266
|
+
branch(*this, total_preferred_at_latest, INT_VAL_MIN);
|
267
|
+
branch(*this, total_not_preferred_at_latest, INT_VAL_MIN);
|
268
|
+
#else // USE_DUMB_BRANCHING
|
269
|
+
# ifdef DEBUG
|
270
|
+
std::cout << "Adding branching (BEST)" << std::endl;
|
271
|
+
std::cout.flush();
|
272
|
+
# endif // DEBUG
|
273
|
+
// This branching is meant to start with most probable solution
|
274
|
+
branch(*this, disabled_package_variables, INT_VAR_SIZE_MIN, INT_VAL_MIN);
|
275
|
+
branch(*this, package_versions, INT_VAR_SIZE_MIN, INT_VAL_MAX);
|
276
|
+
branch(*this, total_required_disabled, INT_VAL_MIN);
|
277
|
+
branch(*this, total_induced_disabled, INT_VAL_MIN);
|
278
|
+
branch(*this, total_suspicious_disabled, INT_VAL_MIN);
|
279
|
+
branch(*this, total_disabled, INT_VAL_MIN);
|
280
|
+
branch(*this, at_latest, INT_VAR_SIZE_MIN, INT_VAL_MAX);
|
281
|
+
branch(*this, total_preferred_at_latest, INT_VAL_MAX);
|
282
|
+
branch(*this, total_not_preferred_at_latest, INT_VAL_MAX);
|
283
|
+
#endif // USE_DUMB_BRANCHING
|
284
|
+
|
285
|
+
#ifdef DEBUG
|
286
|
+
std::cout << "Finalization Done" << std::endl;
|
287
|
+
std::cout.flush();
|
288
|
+
#endif // DEBUG
|
289
|
+
}
|
290
|
+
|
291
|
+
////////////////////////////////////////////////////////////////////////
|
292
|
+
// A general note about constrain functions
|
293
|
+
////////////////////////////////////////////////////////////////////////
|
294
|
+
//
|
295
|
+
// Constrain functions take a space ('best_known_solution') that is has an assignment of variables
|
296
|
+
// and operate in the context of a fresh space, not yet fully assigned. Their purpose is to add
|
297
|
+
// constraints such that the assignments in the fresh space will either yield a better solution, or
|
298
|
+
// none at all if the best_known_solution is the best possible.
|
299
|
+
//
|
300
|
+
|
301
|
+
#ifdef TOTAL_DISABLED_COST
|
302
|
+
//
|
303
|
+
// Very simple constraint function that only minimizes total disabled packages. This is left here
|
304
|
+
// for debugging purposes. Turn this on to test that the basic system can be solved.
|
305
|
+
//
|
306
|
+
void VersionProblem::constrain(const Space & _best_known_solution)
|
307
|
+
{
|
308
|
+
const VersionProblem& best_known_solution = static_cast<const VersionProblem &>(_best_known_solution);
|
309
|
+
|
310
|
+
// add first-level objective function minimization (failing packages, weighted)
|
311
|
+
// new constraint: total_disabled < best_known_total_disabled_value)
|
312
|
+
int best_known_total_disabled_value = best_known_solution.total_disabled.val();
|
313
|
+
rel(*this, total_disabled, IRT_LE, best_known_total_disabled_value);
|
314
|
+
PrintVarAligned("Constrain: total_disabled: ", total_disabled);
|
315
|
+
}
|
316
|
+
#endif // TOTAL_DISABLED_COST
|
317
|
+
|
318
|
+
// _best_known_soln is the most recent satisfying assignment of
|
319
|
+
// variables that Gecode has found. This method examines the solution
|
320
|
+
// and adds additional constraints that are applied after restarting
|
321
|
+
// the search, which means that the next time a solution that's found
|
322
|
+
// must be strictly better than the current best known solution.
|
323
|
+
//
|
324
|
+
// Our model requires us to have a series of objective functions where
|
325
|
+
// each successive objective function is evaluated if and only if all
|
326
|
+
// higher precedent objective functions are tied.
|
327
|
+
//
|
328
|
+
// [TODO: DESCRIBE WHAT THE ACTUAL SERIES OF OBJECTIVE FUNCTIONS IS]
|
329
|
+
//
|
330
|
+
// Lower precedent objective functions are modeled as the consequent
|
331
|
+
// of an implication whose antecedent is the conjunction of all the
|
332
|
+
// higher precedent objective functions being assigned to their best
|
333
|
+
// known value; thus, the optimal value of an objection function
|
334
|
+
// "activates" the next highest objective function. This has the
|
335
|
+
// effect of isolating the logic of each objective function such that
|
336
|
+
// it is only applied to the set of equally preferable solutions under
|
337
|
+
// the higher precedent objective functions. The objective function
|
338
|
+
// then applies its constraints, the solution space is restarted and
|
339
|
+
// walks the space until it finds another, more constrained solution.
|
340
|
+
|
341
|
+
#ifdef VECTOR_CONSTRAIN
|
342
|
+
//
|
343
|
+
// The vector constrain function assembles multiple cost functions into a vector cost, and then
|
344
|
+
// constrains the vector cost to be less than the vector cost of the current best_known_solution.
|
345
|
+
// The less than operation here is a pairwise comparison in order of decreasing precedence; only if
|
346
|
+
// higher precedence elements are tied will the lower precedence elements be consulted. The elements
|
347
|
+
// are in increasing order of precedence.
|
348
|
+
//
|
349
|
+
// In this case the lowest precedence cost is total_not_preferred_at_latest, followed by total_preferred_at_latest
|
350
|
+
// and finally total_disabled.
|
351
|
+
//
|
352
|
+
void VersionProblem::constrain(const Space & _best_known_solution)
|
353
|
+
{
|
354
|
+
const VersionProblem& best_known_solution = static_cast<const VersionProblem &>(_best_known_solution);
|
355
|
+
|
356
|
+
IntVarArgs current(5);
|
357
|
+
IntVarArgs best(5);
|
358
|
+
BuildCostVector(current);
|
359
|
+
best_known_solution.BuildCostVector(best);
|
360
|
+
ConstrainVectorLessThanBest(current, best);
|
361
|
+
}
|
362
|
+
#endif // VECTOR_CONSTRAIN
|
363
|
+
|
364
|
+
void VersionProblem::BuildCostVector(IntVarArgs & costVector) const {
|
365
|
+
costVector[0] = total_not_preferred_at_latest;
|
366
|
+
costVector[1] = total_preferred_at_latest;
|
367
|
+
costVector[2] = total_suspicious_disabled;
|
368
|
+
costVector[3] = total_induced_disabled;
|
369
|
+
costVector[4] = total_required_disabled;
|
370
|
+
}
|
371
|
+
|
372
|
+
|
373
|
+
|
374
|
+
IntVar & VersionProblem::GetPackageVersionVar(int packageId)
|
375
|
+
{
|
376
|
+
if (packageId < cur_package) {
|
377
|
+
return package_versions[packageId];
|
378
|
+
} else {
|
379
|
+
#ifdef DEBUG
|
380
|
+
std::cout << "Bad package Id " << packageId << " >= " << cur_package << std::endl;
|
381
|
+
std::cout.flush();
|
382
|
+
#endif //DEBUG
|
383
|
+
// return 0;
|
384
|
+
}
|
385
|
+
}
|
386
|
+
|
387
|
+
int VersionProblem::GetPackageVersion(int packageId)
|
388
|
+
{
|
389
|
+
IntVar & var = GetPackageVersionVar(packageId);
|
390
|
+
if (1 == var.size()) return var.val();
|
391
|
+
return UNRESOLVED_VARIABLE;
|
392
|
+
}
|
393
|
+
bool VersionProblem::GetPackageDisabledState(int packageId)
|
394
|
+
{
|
395
|
+
return disabled_package_variables[packageId].val() == 1;
|
396
|
+
}
|
397
|
+
|
398
|
+
int VersionProblem::GetMax(int packageId)
|
399
|
+
{
|
400
|
+
return GetPackageVersionVar(packageId).max();
|
401
|
+
}
|
402
|
+
int VersionProblem::GetMin(int packageId)
|
403
|
+
{
|
404
|
+
return GetPackageVersionVar(packageId).min();
|
405
|
+
}
|
406
|
+
|
407
|
+
int VersionProblem::GetDisabledVariableCount()
|
408
|
+
{
|
409
|
+
if (total_disabled.min() == total_disabled.max()) {
|
410
|
+
return total_disabled.min();
|
411
|
+
} else {
|
412
|
+
return UNRESOLVED_VARIABLE;
|
413
|
+
}
|
414
|
+
}
|
415
|
+
|
416
|
+
|
417
|
+
// Utility
|
418
|
+
void VersionProblem::Print(std::ostream & out)
|
419
|
+
{
|
420
|
+
out << "Version problem dump: " << cur_package << "/" << size << " packages used/allocated" << std::endl;
|
421
|
+
out << "Disabled Variables: " << disabled_package_variables << std::endl;
|
422
|
+
out << "Total Disabled variables (required): " << total_required_disabled << std::endl;
|
423
|
+
out << "Total Disabled variables: (induced): " << total_induced_disabled << std::endl;
|
424
|
+
out << "Total Disabled variables: (suspicious): " << total_suspicious_disabled << std::endl;
|
425
|
+
out << "Total Disabled variables: " << total_disabled << std::endl;
|
426
|
+
out << "at_latest: " << at_latest << std::endl;
|
427
|
+
out << "total_preferred_at_latest: " << total_preferred_at_latest << std::endl;
|
428
|
+
out << "total_not_preferred_at_latest: " << total_not_preferred_at_latest << std::endl;
|
429
|
+
for (int i = 0; i < cur_package; i++) {
|
430
|
+
out << "\t";
|
431
|
+
PrintPackageVar(out, i);
|
432
|
+
out << std::endl;
|
433
|
+
}
|
434
|
+
out.flush();
|
435
|
+
}
|
436
|
+
|
437
|
+
// TODO: Validate package ids !
|
438
|
+
|
439
|
+
void VersionProblem::PrintPackageVar(std::ostream & out, int packageId)
|
440
|
+
{
|
441
|
+
IntVar & var = GetPackageVersionVar(packageId);
|
442
|
+
out << "PackageId: " << packageId << " Sltn: " << var << " disabled: " << disabled_package_variables[packageId] << " at latest: " << at_latest[packageId];
|
443
|
+
}
|
444
|
+
|
445
|
+
bool VersionProblem::CheckPackageId(int id)
|
446
|
+
{
|
447
|
+
return (id < size);
|
448
|
+
}
|
449
|
+
|
450
|
+
// We want to sort vectors
|
451
|
+
// This constrains current to be less than best by a process analogous to subtraction
|
452
|
+
// we compute current - best, pairwise with borrows from less significant elements. We require it to be less than zero by requiring the most
|
453
|
+
// significant element to generate a borrow.
|
454
|
+
//
|
455
|
+
void VersionProblem::ConstrainVectorLessThanBest(IntVarArgs & current, IntVarArgs & best) {
|
456
|
+
BoolVarArray borrow(*this, current.size()+1, 0, 1);
|
457
|
+
|
458
|
+
// No borrows can happen at the least significant element.
|
459
|
+
rel(*this, borrow[0], IRT_EQ, 0);
|
460
|
+
|
461
|
+
for (int i = 0; i < current.size(); i++) {
|
462
|
+
// If best+borrow is greater than current (equivalently current-(best+borrow) is < 0) then a more significant element
|
463
|
+
// must have decreased, so we propagate a borrow to the next most significant element.
|
464
|
+
int best_val = best[i].val();
|
465
|
+
IntVar delta = expr(*this, current[i] - best_val - borrow[i]);
|
466
|
+
// (delta < 0) <=> borrow[i+1]
|
467
|
+
rel(*this, delta, IRT_LE, 0, borrow[i+1]);
|
468
|
+
#ifdef DEBUG
|
469
|
+
std::cout << "ConstrainVector: borrow[" << i+1 << "] " << borrow[i+1] << ",\tdelta " << delta << std::endl;
|
470
|
+
std::cout << "ConstrainVector: current[" << i << "] " << current[i] << ",\tbest_val " << best_val << std::endl;
|
471
|
+
#endif //DEBUG
|
472
|
+
}
|
473
|
+
|
474
|
+
// must borrow off past the most significant element.
|
475
|
+
rel(*this, borrow[current.size()], IRT_EQ, 1);
|
476
|
+
}
|
477
|
+
|
478
|
+
VersionProblem * VersionProblem::Solve(VersionProblem * problem)
|
479
|
+
{
|
480
|
+
problem->Finalize();
|
481
|
+
problem->status();
|
482
|
+
#ifdef DEBUG
|
483
|
+
std::cout << "Before solve" << std::endl;
|
484
|
+
problem->Print(std::cout);
|
485
|
+
#endif //DEBUG
|
486
|
+
int i = 0;
|
487
|
+
|
488
|
+
Gecode::Support::Timer timer;
|
489
|
+
VersionProblem *best_solution = NULL;
|
490
|
+
timer.start();
|
491
|
+
|
492
|
+
for (int k = 0; k < 1; k++)
|
493
|
+
{
|
494
|
+
|
495
|
+
Restart<VersionProblem> solver(problem);
|
496
|
+
best_solution = NULL;
|
497
|
+
|
498
|
+
while (VersionProblem *solution = solver.next())
|
499
|
+
{
|
500
|
+
if (best_solution != NULL)
|
501
|
+
{
|
502
|
+
delete best_solution;
|
503
|
+
}
|
504
|
+
best_solution = solution;
|
505
|
+
++i;
|
506
|
+
#ifdef DEBUG
|
507
|
+
std::cout << "Trial Solution #" << i << "===============================" << std::endl;
|
508
|
+
const Search::Statistics & stats = solver.statistics();
|
509
|
+
std::cout << "Solver stats: Prop:" << stats.propagate << " Fail:" << stats.fail << " Node:" << stats.node;
|
510
|
+
std::cout << " Depth:" << stats.depth << " memory:" << stats.memory << std::endl;
|
511
|
+
solution->Print(std::cout);
|
512
|
+
#endif //DEBUG
|
513
|
+
}
|
514
|
+
|
515
|
+
}
|
516
|
+
|
517
|
+
double elapsed_time = timer.stop();
|
518
|
+
#ifdef DEBUG_LITE
|
519
|
+
std::cout << "Solution completed: " << (best_solution ? "Found solution" : "No solution found") << std::endl;
|
520
|
+
std::cout << "Solution consumed: " << elapsed_time << " ms " << i << " steps" << std::endl;
|
521
|
+
std::cout << "======================================================================" << std::endl;
|
522
|
+
std::cout.flush();
|
523
|
+
#endif // DEBUG_LITE
|
524
|
+
|
525
|
+
return best_solution;
|
526
|
+
}
|
527
|
+
|
528
|
+
//
|
529
|
+
// Debug output
|
530
|
+
//
|
531
|
+
template <class T> void PrintVarAligned(const char * message, T & var)
|
532
|
+
{
|
533
|
+
#ifdef DEBUG
|
534
|
+
std::cout.width(40);
|
535
|
+
std::cout << std::left << message << var << std::endl;
|
536
|
+
std::cout.width(0);
|
537
|
+
#endif
|
538
|
+
}
|
539
|
+
template <class S, class T> void PrintVarAligned(const char * message, S & var1, T & var2)
|
540
|
+
{
|
541
|
+
#ifdef DEBUG
|
542
|
+
std::cout.width(40);
|
543
|
+
std::cout << std::left << message << var1 << " " << var2 << std::endl;
|
544
|
+
std::cout.width(0);
|
545
|
+
#endif
|
546
|
+
}
|
547
|
+
|
548
|
+
//template void PrintVarAligned<int>(const char * message, int & var);
|
549
|
+
|
550
|
+
|
551
|
+
|
552
|
+
//
|
553
|
+
// Version Problem
|
554
|
+
//
|
555
|
+
//
|
556
|
+
//
|
557
|
+
//
|