Hi all,
In my project that I'm working on, I seem to get strange errors, but I
cannot seem to be able to figure out why, so I figured perhaps you might
give some insight into the problem.
I'm attaching a simplified version (but not a minimal example) of the code
to where I have tracked down the problem. I don't want to force you to sift
through tons of code. I included the most relevant parts only. I can
provide the full source (it isn't that big) upon request, though. That is
fine.
My best guess is that there is some memory corruption - some memory getting
freed and is then overwritten somewhere. But it might be something else
entirely - like I said, I'm not sure because my results are conflicting.
I have the tracked the problem down to being the m_rectangle variable. The
culprit seems to be the line
r1 = std::move(m_rectangles.v().back());
If I remove it, all works fine. I have tried using both a Gecode space
allocator and the normal standard allocator - both produce the same thing.
Because it's memory corruption or something, the bug appears differently
depending on how the code is run - never in a single place (unless run
under the same parameters).
Am I doing this right? Am I missing something? I just can't see what. I
have been trying to wrap my head around this for a long time now, but I
can't seem to find anything.
I hate to ask you to go through a lot of code, but I'm stuck here, so I'd
appreciate a little insight -- any insight.
I just hope the mailing list accepts attachments. I would hate to have to
provide source inline.
#include <gecode/int.hh>
#include <array>
#include <iterator>
#include <algorithm>
#include <assert.h>
#include <vector>
#include <iostream>
#include <assert.h>
// The no-overlap propagator
class NoOverlap: public Gecode::Propagator
{
protected:
struct XRectangle2
{
Gecode::Int::IntView* x;
Gecode::Int::IntView* y;
int width, height;
};
// The x and y coordinates
Gecode::ViewArray<Gecode::Int::IntView> m_x, m_y;
// The width and height arrays
XGecodeVector2<int> m_width, m_height;
XGecodeVector2<XRectangle2> m_rectangles;
public:
NoOverlap(Gecode::Home home,
Gecode::ViewArray<Gecode::Int::IntView>& x, XGecodeVector2<int>& width,
Gecode::ViewArray<Gecode::Int::IntView>& y, XGecodeVector2<int>& height):
Gecode::Propagator(home), m_x(x), m_y(y), m_width(std::move(width)), m_height(std::move(height)),
m_rectangles(home)
{
m_x.subscribe(home, *this, Gecode::Int::PC_INT_BND);
m_y.subscribe(home, *this, Gecode::Int::PC_INT_BND);
for (size_t i = 0; i < m_x.size(); i++)
m_rectangles.v().push_back(CreateRectangle2(i));
}
NoOverlap(Gecode::Space& home, bool Share, NoOverlap& that): Gecode::Propagator(home, Share, that),
m_x(that.m_x), m_y(that.m_y), m_width(home, that.m_width), m_height(home, that.m_height),
m_rectangles(home)
{
m_x.update(home, Share, that.m_x);
m_y.update(home, Share, that.m_y);
for (size_t i = 0; i < m_x.size(); i++)
m_rectangles.v().push_back(CreateRectangle2(i));
}
// Return cost (defined as cheap quadratic)
virtual Gecode::PropCost cost(const Gecode::Space&, const Gecode::ModEventDelta&) const
{
return Gecode::PropCost::quadratic(Gecode::PropCost::LO,2*m_x.size());
}
template<typename... T>
static Gecode::ExecStatus post(Gecode::Home home, Gecode::PropCond, Gecode::ViewArray<Gecode::Int::IntView> x, T&&... args)
{
// Only if there is something to propagate
if (x.size() > 1)
{
NoOverlap* ThisPtr = new (home) NoOverlap(home, x, args...);
#ifdef _DEBUG
//home.notice(*ThisPtr, Gecode::AP_DISPOSE);
#endif
}
return Gecode::ES_OK;
}
virtual Propagator* copy(Gecode::Space& home, bool share)
{
return new (home) NoOverlap(home, share, *this);
}
virtual size_t dispose(Gecode::Space& home)
{
m_x.cancel(home, *this, Gecode::Int::PC_INT_BND);
m_y.cancel(home, *this, Gecode::Int::PC_INT_BND);
Propagator::dispose(home);
return sizeof(*this);
}
private:
XRectangle2 CreateRectangle2(size_t var_id)
{
XRectangle2 rect = { &m_x[(int)var_id], &m_y[(int)var_id],
m_width.v().at(var_id), m_height.v().at(var_id) };
assert(rect.width > 0);
assert(rect.height > 0);
assert(rect.width == rect.height);
return rect;
}
virtual Gecode::ExecStatus propagate(Gecode::Space& home, const Gecode::ModEventDelta&)
{
size_t NumVars = (size_t)m_rectangles.v().size();
// This variable remembers if any propagation was done or not. Used to know if we are at fixpoint or not.
bool PropagationDone = false;
// For every variable...
for (size_t idx_r1 = 0; idx_r1 < NumVars - 1; idx_r1++)
{
// If true if the current square (idx_r1) cannot overlap with any other square.
bool CannotOverlap = false;
// For every other variable + symmetry breaking
// Consider: When checking square 0, we want to start checking from square 1 since comparing against itself is pointless.
// Then, consider when checking square 1, due to symmetry, we don't need to check against square 0 (already did it).
// Generalizing this, square k has already been checked for squares 0...k-1, so we can start from k+1.
for (size_t idx_r2 = idx_r1 + 1; idx_r2 < NumVars; idx_r2++)
{
CannotOverlap = true;
const auto & r1 = m_rectangles.v().at(idx_r1);
const auto & r2 = m_rectangles.v().at(idx_r2);
// Helper function that checks result of propagation. Updates propagation variable if propagation was done.
// Fails if the propagation failed.
auto CheckResult = [&](Gecode::ModEvent status)
{
switch (status)
{
case Gecode::Int::ME_INT_NONE: PropagationDone = PropagationDone || false; break;
case Gecode::Int::ME_INT_FAILED: return Gecode::ES_FAILED;
default: PropagationDone = true; break;
}
return Gecode::ES_OK;
};
if (!CanBeAbove && !CanBeBelow && !CanBeLeft && CanBeRight)
result = CheckResult( r2.x->gq(home, r1.x->min() + r1.width) );
else if (!CanBeAbove && !CanBeBelow && CanBeLeft && !CanBeRight)
result = CheckResult( r2.x->lq(home, r1.x->max() - r2.width) );
else if (!CanBeAbove && CanBeBelow && !CanBeLeft && !CanBeRight)
result = CheckResult( r2.y->gq(home, r1.y->min() + r1.height) );
else if (CanBeAbove && !CanBeBelow && !CanBeLeft && !CanBeRight)
result = CheckResult( r2.y->lq(home, r1.y->max() - r2.height) );
if (result == Gecode::ES_FAILED)
return Gecode::ES_FAILED;
}
if (CannotOverlap)
{
auto & r1 = m_rectangles.v().at(idx_r1);
r1.x->cancel(home, *this, Gecode::Int::PC_INT_BND);
r1.y->cancel(home, *this, Gecode::Int::PC_INT_BND);
r1 = std::move(m_rectangles.v().back());
m_rectangles.v().pop_back();
if (--NumVars == 1) // Subsumption achieved
break;
// Restart loop again
idx_r1--;
}
}
if (m_rectangles.v().size() == 1)
return home.ES_SUBSUMED(*this);
else
return (PropagationDone ? Gecode::ES_NOFIX : Gecode::ES_FIX);
}
};
void nooverlap(Gecode::Home home,
const Gecode::IntVarArgs& x, const Gecode::IntArgs& width,
const Gecode::IntVarArgs& y, const Gecode::IntArgs& height)
{
// Check whether the arguments make sense
if ((x.size() != y.size()) || (x.size() != width.size()) ||
(y.size() != height.size()))
throw Gecode::Int::ArgumentSizeMismatch("nooverlap");
assert((size_t)x.size() == (size_t)y.size() && (size_t)y.size() == (size_t)width.size() &&
(size_t)width.size() == (size_t)height.size());
CreatePropagator<NoOverlap>(home, Gecode::Int::PC_INT_BND, x, width, y, height);
}
template<typename T, template<typename> class Allocator = Gecode::space_allocator>
class XGecodeVector2
{
private:
std::vector<T, Allocator<T>> m_v;
public:
std::vector<T, Allocator<T>>& v() { return m_v; };
template<typename... Args_t>
XGecodeVector2(Gecode::Home home, Args_t... Args): m_v(Args..., Allocator<T>(home)) {}
XGecodeVector2(Gecode::Space& home, const XGecodeVector2& that): m_v(that.m_v.size(), T(), Allocator<T>(home)), m_valid(that.m_valid)
{
// Apparently Mingw/Clang does not implement std::vector<T, Allocator<T>>(const std::vector<T>&, Allocator<T>)...
// So we have to use a workaround!
std::copy(that.m_v.begin(), that.m_v.end(), m_v.begin());
}
XGecodeVector2(XGecodeVector2&& that): m_v(std::move(that.m_v)) {}
};
template<typename T>
XGecodeVector2<T> CreateVector(int Width, const T& elem, Gecode::Space& home)
{
return XGecodeVector2<T>(home, Width, elem);
}
template<typename T>
XGecodeVector2<int> CreateVector(int Width, const T& elem, Gecode::Home& home)
{
return CreateVector(Width, elem, static_cast<Gecode::Space&>(home));
}
inline Gecode::ViewArray<Gecode::Int::IntView> Clone(Gecode::Home home, const Gecode::IntVarArgs& array)
{
return Gecode::ViewArray<Gecode::Int::IntView>(home, array);
}
inline XGecodeVector2<int> Clone(Gecode::Home home, const Gecode::IntArgs& array)
{
auto && vec = CreateVector(array.size(), 0, home);
std::copy(array.begin(), array.end(), vec.v().begin()); // Not a problem: space has been allocated
return std::move(vec);
}
template<typename Propagator_t, typename... T>
void CreatePropagator(Gecode::Home home, Gecode::PropCond prop_cond, T&... args)
{
// Never post a propagator in a failed space
if (home.failed()) return;
// If posting failed, fail space
if (Propagator_t::post(home, prop_cond, Clone(home, args)...) != Gecode::ES_OK)
home.fail();
}
_______________________________________________
Gecode users mailing list
users@gecode.org
https://www.gecode.org/mailman/listinfo/gecode-users