dep_selector 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
//
|