Hi,
No, it's not entirely self contained. It was just a dumbed down cut and
paste in order to see if anyone could find some quick glaring errors.
Anyway, here is a minimal example that produced the bug - the crash, at
least as far as I could conveniently reduce it to.
To reproduce the crash: run Gist to find all solutions (press "A"). Then
double-click twice on the solution node (the yellow node).
The program will crash.
On 28 May 2013 22:52, Guido Tack <t...@gecode.org> wrote:
> Hi,
>
> your sample code does not compile, are you sure it's self contained?
> E.g. XGecodeVector2 initialises m_valid which is not defined (and there
> seem to be other problems).
>
> Cheers,
> Guido
>
> --
> Guido Tack
> http://www.csse.monash.edu/~guidot/
>
>
>
> On 18/05/2013, at 11:33 AM, Mailing List Email <mailingli...@gmail.com>
> wrote:
>
> 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.
> <Temp.cpp>_______________________________________________
> Gecode users mailing list
> users@gecode.org
> https://www.gecode.org/mailman/listinfo/gecode-users
>
>
>
/*
* Main author:
* Christian Schulte <cschu...@kth.se>
*
* Copyright:
* Christian Schulte, 2009
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* Em_xPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#include <gecode/int.hh>
#include <vector>
#include <algorithm>
template<typename T, template<typename> class Allocator = Gecode::space_allocator>
class XGecodeVector2
{
private:
std::vector<T, Allocator<T>> m_v;
bool m_valid;
public:
std::vector<T, Allocator<T>>& v() { assert(m_valid); return m_v; };
template<typename... Args_t>
XGecodeVector2(Gecode::Region& region, Args_t... Args): m_v(Args..., Allocator<T>(region)), m_valid(true) {}
template<typename... Args_t>
XGecodeVector2(Gecode::Home home, Args_t... Args): m_v(Args..., Allocator<T>(home)), m_valid(true) {}
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)), m_valid(true) {}
~XGecodeVector2() { m_valid = false; }
};
// 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;
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_x.subscribe(home, *this, Gecode::Int::PC_INT_BND);
m_y.subscribe(home, *this, Gecode::Int::PC_INT_BND);
}
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_x.update(home, Share, that.m_x);
m_y.update(home, Share, that.m_y);
}
// 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)
new (home) NoOverlap(home, x, args...);
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:
// Holds all information about a specific square
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;
}
struct PropagationResult
{
bool PropagationDone;
Gecode::ExecStatus Status;
};
template<typename CheckResult_t>
PropagationResult PropagateImpl(CheckResult_t, PropagationResult& Result)
{
return Result;
}
template<typename Propagator_t, typename CheckResult_t, typename... Propagators_t>
PropagationResult PropagateImpl(CheckResult_t CheckResult, PropagationResult& Result, Propagator_t&& propagator, Propagators_t&&... propagators)
{
if ( !CheckResult(propagator()) ) return Result;
return PropagateImpl(CheckResult, Result, propagators...);
}
template<typename... Propagators_t>
PropagationResult Propagate(Propagators_t&&... propagators)
{
// Exceptions are ideal, but too expensive.
PropagationResult Result;
auto CheckResult = [&](Gecode::ModEvent status)
{
switch (status)
{
case Gecode::Int::ME_INT_NONE: Result.PropagationDone = Result.PropagationDone || false; break;
case Gecode::Int::ME_INT_FAILED: Result.Status = Gecode::ES_FAILED; return false;
default: Result.PropagationDone = true; break;
}
Result.Status = Gecode::ES_OK;
return true;
};
return PropagateImpl(CheckResult, Result, propagators...);
}
// Perform propagation
// NOTE: Assumes bound consistency! The domains of x and y shall be contiguous!
virtual Gecode::ExecStatus propagate(Gecode::Space& home, const Gecode::ModEventDelta&)
{
size_t NumVars = (size_t)m_x.size();
// This variable contains the number of non-overlapping squares detected during propagation. Used to determine if
// subsumption has been hit or not.
// This variable remembers if any propagation was done or not. Used to know if we are at fixpoint or not.
PropagationResult result = { false, Gecode::ES_OK };
// For every variable...
for (size_t idx_r1 = 0; idx_r1 < m_x.size() - 1; idx_r1++)
{
// If true if the current square (idx_r1) cannot overlap with any other square.
bool CannotOverlap = false;
const auto & r1 = CreateRectangle2(idx_r1);
int oblig_x1 = r1.x->max();
int oblig_x2 = r1.x->min() + r1.width - 1;
int oblig_y1 = r1.y->max();
int oblig_y2 = r1.y->min() + r1.height - 1;
bool ObligatoryExists = (oblig_x1 >= 0 && oblig_y1 >= 0 && oblig_x1 <= oblig_x2 && oblig_y1 <= oblig_y2);
// 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 < m_x.size(); idx_r2++)
{
CannotOverlap = true;
const auto & r2 = CreateRectangle2(idx_r2);
bool IsAbove, IsBelow, IsLeft, IsRight;
bool CanBeAbove, CanBeBelow, CanBeLeft, CanBeRight;
if (ObligatoryExists)
{
int width = oblig_x2 - oblig_x1 + 1;
int height = oblig_y2 - oblig_y1 + 1;
IsAbove = (r2.y->max() <= oblig_y1 - r2.height);
IsBelow = (r2.y->min() >= oblig_y2 + height);
IsLeft = (r2.x->max() <= oblig_x1 - r2.width);
IsRight = (r2.x->min() >= oblig_x2 + width);
CanBeAbove = (r2.y->min() <= oblig_y1 - r2.height);
CanBeBelow = (r2.y->max() > oblig_y2);
CanBeLeft = (r2.x->min() <= oblig_x1 - r2.width);
CanBeRight = (r2.x->max() > oblig_x2);
}
else
{
IsAbove = (r2.y->max() <= r1.y->min() - r2.height);
IsBelow = (r2.y->min() >= r1.y->max() + r1.height);
IsLeft = (r2.x->max() <= r1.x->min() - r2.width);
IsRight = (r2.x->min() >= r1.x->max() + r1.width);
CanBeAbove = (r2.y->min() <= r1.y->max() - r2.height);
CanBeBelow = (r2.y->max() >= r1.y->min() + r1.height);
CanBeLeft = (r2.x->min() <= r1.x->max() - r2.width);
CanBeRight = (r2.x->max() >= r1.x->min() + r1.width);
}
bool CannotOverlapTmp = (IsAbove || IsBelow || IsLeft || IsRight);
CannotOverlap = CannotOverlap && CannotOverlapTmp;
// Propagate and update statistics
if (!CanBeAbove && !CanBeBelow && !CanBeLeft && !CanBeRight)
return Gecode::ES_FAILED;
// 1x1 square optimization. If it does anything, it sure doesn't show.
if (idx_r2 == NumVars - 1)
continue; // This is the smallest variable. Don't propagate for this one.
// Helper function that checks result of propagation. Updates propagation variable if propagation was done.
// Fails if the propagation failed.
if (!CanBeAbove && !CanBeBelow && !CanBeLeft && CanBeRight)
// Symmetry: if r2 can be right of r1, then r1 can be left of r2. Same for all other cases.
result = Propagate(
[&] { return r2.x->gq(home, r1.x->min() + r1.width); },
[&] { return r1.x->lq(home, r2.x->max() - r1.width); }
);
else if (!CanBeAbove && !CanBeBelow && CanBeLeft && !CanBeRight)
result = Propagate(
[&] { return r2.x->lq(home, r1.x->max() - r2.width); },
[&] { return r1.x->gq(home, r2.x->min() + r2.width); }
);
else if (!CanBeAbove && CanBeBelow && !CanBeLeft && !CanBeRight)
result = Propagate(
[&] { return r2.y->gq(home, r1.y->min() + r1.height); },
[&] { return r1.y->lq(home, r2.y->max() - r1.height); }
);
else if (CanBeAbove && !CanBeBelow && !CanBeLeft && !CanBeRight)
result = Propagate(
[&] { return r2.y->lq(home, r1.y->max() - r2.height); },
[&] { return r1.y->gq(home, r2.y->min() + r2.height); }
);
if (result.Status == Gecode::ES_FAILED)
return Gecode::ES_FAILED;
}
if (CannotOverlap)
{
r1.x->cancel(home, *this, Gecode::Int::PC_INT_BND);
r1.y->cancel(home, *this, Gecode::Int::PC_INT_BND);
if (--NumVars == 1) // Subsumption achieved
break;
}
}
if (NumVars == 1)
return home.ES_SUBSUMED(*this);
else
return (result.PropagationDone ? Gecode::ES_NOFIX : Gecode::ES_FIX);
}
virtual void operator = (const NoOverlap&) {}
};
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();
}
/*
* Post the constraint that the rectangles defined by the coordinates
* x and m_y and m_width w and m_height h do not overlap.
*
* This is the function that you will call from your model. The best
* is to paste the entire file into your model.
*/
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");
CreatePropagator<NoOverlap>(home, Gecode::Int::PC_INT_BND, x, width, y, height);
}
// Square Packing.cpp : Defines the entry point for the console application.
//
#include <gecode/int.hh>
#include <gecode/search.hh>
#include <gecode/gist.hh>
#include <gecode/driver.hh>
#include <gecode/minimodel.hh>
#include <type_traits>
#include <array>
#include <cmath>
void nooverlap(Gecode::Home home,
const Gecode::IntVarArgs& x, const Gecode::IntArgs& width,
const Gecode::IntVarArgs& y, const Gecode::IntArgs& height);
namespace Impl
{
// Calculate upper bound for s and coordinates (x, y)
template<size_t N>
struct CalcUpperSizeBound
{
static const size_t val = N + CalcUpperSizeBound<N - 1>::val;
};
template<>
struct CalcUpperSizeBound<0>
{
static const size_t val = 0;
};
}
template<int N>
class XSquarePacking: public Gecode::MinimizeScript //: public XGecodeBase<XSquarePacking<N>, Gecode::MinimizeScript>
{
private:
static_assert(N > 0, "N must be greater than 0.");
typedef typename std::remove_const<decltype(N)>::type It_t;
// Variables
Gecode::IntVarArray m_x, m_y;
Gecode::IntVar m_s;
// Return the size of a squares, where VarIndex is the index of the square
It_t SizeOfSquare(size_t VarIndex) const
{
typedef It_t value_type;
static std::array<value_type, N> SizeTbl;
static bool TblInitialized = false;
if (!TblInitialized)
{
for (size_t ArrayIdx = 0, Size = N; ArrayIdx < SizeTbl.size(); ArrayIdx++, Size--)
SizeTbl.at(ArrayIdx) = static_cast<value_type>(Size);
TblInitialized = true;
}
return SizeTbl.at(VarIndex);
}
public:
// Worst case scenario: all squares are packed vertically or horizontally.
// In that case, we need 1 + 2 + ... + N units of width or length to fit them
// all.
// The minimal size of s must be at least N because otherwise the square of
// length NxN cannot fit. Likewise, it must be N + N-1, otherwise we cannot
// fit the NxN square AND the N-1 square because there must be at least
// N + N-1 units of length available on either the width or height.
XSquarePacking(const Gecode::Options&):
m_x(*this, N, 0, Impl::CalcUpperSizeBound<N>::val),
m_y(*this, N, 0, Impl::CalcUpperSizeBound<N>::val),
m_s(*this, (int)std::ceil(std::sqrt((N*(N + 1)*(2*N + 1))/6)), Impl::CalcUpperSizeBound<N>::val)
{
auto PropagationStrength = Gecode::ICL_VAL;
// Make sure no squares overlap (no overlap propagator)
// <--------------- BLOCK A START
auto SizeTbl = Gecode::IntArgs::create(N, N, -1);
::nooverlap(*this, m_x, SizeTbl, m_y, SizeTbl);
//Gecode::nooverlap(*this, m_x.c, SizeTbl, m_y.c, SizeTbl, Gecode::ICL_BND);
// ---------------> BLOCK A END
// Make sure that all squares stay inside s.
for (It_t i = 0; i < N; i++)
{
Gecode::rel(*this, m_x[i] + SizeOfSquare(i) <= m_s, PropagationStrength);
Gecode::rel(*this, m_y[i] + SizeOfSquare(i) <= m_s, PropagationStrength);
}
// Branch on s, then x0, y1, x1, y1 ... xN-1, yN-1
Gecode::branch(*this, m_s, Gecode::INT_VAL_MIN());
for (It_t i = 0; i < m_x.size(); i++)
{
Gecode::branch(*this, m_y[i], Gecode::INT_VAL_SPLIT_MIN());
Gecode::branch(*this, m_x[i], Gecode::INT_VAL_SPLIT_MIN());
}
}
XSquarePacking(XSquarePacking<N>& that, bool share): Gecode::MinimizeScript(share, that)
{
m_x.update(*this, share, that.m_x);
m_y.update(*this, share, that.m_y);
m_s.update(*this, share, that.m_s);
}
virtual Gecode::Space* copy(bool share)
{
return new XSquarePacking<N>(*this, share);
}
virtual Gecode::IntVar cost() const { return m_s; }
};
int main()
{
typedef XSquarePacking<8> Model_t;
Gecode::Options opt("Square Packing");
opt.mode(Gecode::SM_GIST);
Gecode::MinimizeScript::run<Model_t, Gecode::BAB>(opt);
return 0;
}
_______________________________________________
Gecode users mailing list
users@gecode.org
https://www.gecode.org/mailman/listinfo/gecode-users