/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ /* * Main authors: * Mikael Lagerkvist * * Copyright: * Mikael Lagerkvist, 2005 * * Last modified: * $Date: 2008-08-20 18:29:46 +0200 (Wed, 20 Aug 2008) $ by $Author: tack $ * $Revision: 7668 $ * * This file is part of Gecode, the generic constraint * development environment: * http://www.gecode.org * * 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, * EXPRESS 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. * */ /* This is the propagation algorithm of the cumulatives constraint as presented in @inproceedings{DBLP:conf/cp/BeldiceanuC02, author = {Nicolas Beldiceanu and Mats Carlsson}, title = {A New Multi-resource cumulatives Constraint with Negative Heights.}, booktitle = {CP}, year = {2002}, pages = {63-79}, ee = {http://link.springer.de/link/service/series/0558/bibs/2470/24700063.htm}, crossref = {DBLP:conf/cp/2002}, bibsource = {DBLP, http://dblp.uni-trier.de} } @proceedings{DBLP:conf/cp/2002, editor = {Pascal Van Hentenryck}, title = {Principles and Practice of Constraint Programming - CP 2002, 8th International Conference, CP 2002, Ithaca, NY, USA, September 9-13, 2002, Proceedings}, booktitle = {CP}, publisher = {Springer}, series = {Lecture Notes in Computer Science}, volume = {2470}, year = {2002}, isbn = {3-540-44120-4}, bibsource = {DBLP, http://dblp.uni-trier.de} } */ #include #include #include namespace Gecode { namespace Int { namespace Cumulatives { template forceinline Val::Val(Space* home, const ViewArray& _machine, const ViewArray& _start, const ViewArray& _duration, const ViewArray& _end, const ViewArray& _height, const IntArgs& _limit, bool _at_most) : Propagator(home), machine(_machine), start(_start), duration(_duration), end(_end), height(_height), limit(_limit.size()), at_most(_at_most) { force(home); for(int i = _limit.size(); i--; ) limit[i] = _limit[i]; machine.subscribe(home,this,PC_INT_DOM); start.subscribe(home,this,PC_INT_BND); duration.subscribe(home,this,PC_INT_BND); end.subscribe(home,this,PC_INT_BND); height.subscribe(home,this,PC_INT_BND); } template ExecStatus Val ::post(Space* home, const ViewArray& machine, const ViewArray& start, const ViewArray& duration, const ViewArray& end, const ViewArray& height, const IntArgs& limit, bool at_most) { (void) new (home) Val(home, machine, start, duration, end, height, limit, at_most); return ES_OK; } template forceinline Val::Val(Space* home, bool share, Val& p) : Propagator(home,share,p), at_most(p.at_most) { machine.update(home,share,p.machine); start.update(home, share, p.start); duration.update(home, share, p.duration); end.update(home, share, p.end); height.update(home, share, p.height); limit.update(home, share, p.limit); } template size_t Val::dispose(Space* home) { unforce(home); if (!home->failed()) { machine.cancel(home,this,PC_INT_DOM); start.cancel(home,this,PC_INT_BND); duration.cancel(home,this,PC_INT_BND); end.cancel(home,this,PC_INT_BND); height.cancel(home,this,PC_INT_BND); } limit.~SharedArray(); (void) Propagator::dispose(home); return sizeof(*this); } template PropCost Val::cost(ModEventDelta) const { return cost_hi(start.size(), PC_QUADRATIC_LO); } template Support::Symbol Val::ati(void) { return Reflection::mangle("Gecode::Int::Cumulatives::Val"); } template Reflection::ActorSpec Val::spec(const Space* home, Reflection::VarMap& m) const { Reflection::ActorSpec s(ati()); return s << machine.spec(home, m) << start.spec(home, m) << duration.spec(home, m) << end.spec(home, m) << height.spec(home, m) << Reflection::Arg::newIntArray(limit) << at_most; } template void Val::post(Space* home, Reflection::VarMap& vars, const Reflection::ActorSpec& spec) { spec.checkArity(7); ViewArray machine(home, vars, spec[0]); ViewArray start(home, vars, spec[1]); ViewArray duration(home, vars, spec[2]); ViewArray end(home, vars, spec[3]); ViewArray height(home, vars, spec[4]); Reflection::IntArrayArg* limitA = spec[5]->toIntArray(); IntArgs limit(limitA->size()); for (int i=limitA->size(); i--; ) limit[i] = (*limitA)[i]; bool at_most = spec[6]->toInt(); (void) new (home) Val(home,machine,start,duration, end,height,limit,at_most); } template Actor* Val::copy(Space* home, bool share) { return new (home) Val(home,share,*this); } /// Types of events for the sweep-line typedef enum {EVENT_CHCK, EVENT_PROF, EVENT_PRUN} ev_t; /// An event collects the information for one evnet for the sweep-line class Event { public: /// The type of the event ev_t e; /// The task this event refers to int task; /// The date of this event int date; /// The quantity changed by this event (if any) int inc; /** If the type is EVENT_PROF and it is the first of the pair, * this value is true. Added to handle contribution-values * correctly for both at_most and at_least. */ bool first_prof; /// Simple constructor Event(ev_t _e, int _task, int _date, int _inc = 0, bool _first_prof = false) : e(_e), task(_task), date(_date), inc(_inc), first_prof(_first_prof) {} /// Order events based on date. bool operator<(const Event& ev) const { if (date == ev.date) { if (e == EVENT_PROF && ev.e != EVENT_PROF) return true; if (e == EVENT_CHCK && ev.e == EVENT_PRUN) return true; return false; } return date < ev.date; } }; template ExecStatus Val::prune(Space * home, int low, int up, int r, int ntask, int sheight, int* contribution, int* prune_tasks, int& prune_tasks_size) { if (ntask > 0 && (at_most ? sheight > limit[r] : sheight < limit[r])) { return ES_FAILED; } // std::list::iterator it = prune_tasks.begin(); // while (it != prune_tasks.end()) { // int t = *it; int pti = 0; while (pti != prune_tasks_size) { int t = prune_tasks[pti]; // Algorithm 2. // Prune the machine, start, and end for required // tasks for machine r that have heights possibly greater than 0. if (ntask != 0 && (at_most ? height[t].min() < 0 : height[t].max() > 0) && (at_most ? sheight-contribution[t] > limit[r] : sheight-contribution[t] < limit[r])) { if (me_failed(machine[t].eq(home, r))|| me_failed(start[t].gq(home, up-duration[t].max()+1))|| me_failed(start[t].lq(home, low))|| me_failed(end[t].lq(home,low+duration[t].max()))|| me_failed(end[t].gq(home, up+1))|| me_failed(duration[t].gq(home,std::min(up-start[t].max()+1, end[t].min()-low))) ) { return ES_FAILED; } } // Algorithm 3. // Remove values that prevent us from reaching the limit if (at_most ? height[t].min() > std::min(0, limit[r]) : height[t].max() < std::max(0, limit[r])) { if (at_most ? sheight-contribution[t]+height[t].min() > limit[r] : sheight-contribution[t]+height[t].max() < limit[r]) { if (end[t].min() > low && start[t].max() <= up && duration[t].min() > 0) { if (me_failed(machine[t].nq(home, r))) { return ES_FAILED; } } else if (machine[t].assigned()) { int dtmin = duration[t].min(); if (dtmin > 0) { Iter::Ranges::Singleton a(low-dtmin+1, up), b(low+1, up+dtmin); if (me_failed(start[t].minus_r(home,a,false)) || me_failed(end[t].minus_r(home, b,false))) { return ES_FAILED; } } if (me_failed(duration[t].lq(home, std::max(std::max(low-start[t].min(), end[t].max()-up-1), 0)))) { return ES_FAILED; } } } } // Algorithm 4. // Remove bad negative values from for-sure heights. /* \note "It is not sufficient to only test for assignment of machine[t] * since Algorithm 3 can remove the value." Check this against * the conditions for Alg3. */ if (machine[t].assigned() && machine[t].val() == r && end[t].min() > low && start[t].max() <= up && duration[t].min() > 0 ) { if (me_failed(at_most ? height[t].lq(home, limit[r]-sheight+contribution[t]) : height[t].gq(home, limit[r]-sheight+contribution[t]))) { return ES_FAILED; } } // Remove tasks that are no longer relevant. if ((!machine[t].in(r)) || end[t].max() <= up+1) { prune_tasks[pti] = prune_tasks[--prune_tasks_size]; } else { ++pti; } } return ES_OK; } namespace { template class LT { public: bool operator()(const C& lhs, const C& rhs) { return lhs < rhs; } }; } template ExecStatus Val::propagate(Space* home, ModEventDelta) { // Check for subsumption bool subsumed = true; for (int t = start.size(); t--; ) if (!(duration[t].assigned() && end[t].assigned() && machine[t].assigned() && start[t].assigned() && height[t].assigned())) { subsumed = false; break; } // Propagate information for machine r for (int r = limit.size(); r--; ) { // std::list events; GECODE_AUTOARRAY(Event, events, start.size()*8); int events_size = 0; #define GECODE_PUSH_EVENTS(E) assert(events_size std::min(0, limit[r]) : height[t].max() < std::max(0, limit[r])) { GECODE_PUSH_EVENTS(Event(EVENT_CHCK, t, start[t].max(), 1)); GECODE_PUSH_EVENTS(Event(EVENT_CHCK, t, end[t].min(), -1)); } if (at_most ? height[t].min() > 0 : height[t].max() < 0) { GECODE_PUSH_EVENTS(Event(EVENT_PROF, t, start[t].max(), at_most ? height[t].min() : height[t].max(), true)); GECODE_PUSH_EVENTS(Event(EVENT_PROF, t, end[t].min(), at_most ? -height[t].min() : -height[t].max())); } } if (machine[t].in(r)) { if (at_most ? height[t].min() < 0 : height[t].max() > 0) { GECODE_PUSH_EVENTS(Event(EVENT_PROF, t, start[t].min(), at_most ? height[t].min() : height[t].max(), true)); GECODE_PUSH_EVENTS(Event(EVENT_PROF, t, end[t].max(), at_most ? -height[t].min() : -height[t].max())); } if (!(machine[t].assigned() && height[t].assigned() && start[t].assigned() && end[t].assigned())) { GECODE_PUSH_EVENTS(Event(EVENT_PRUN, t, start[t].min())); } } } #undef GECODE_PUSH_EVENTS // If there are no events, continue with next machine if (events_size == 0) continue; // Sort the events according to date LT lt; Support::insertion(&events[0], events_size, lt); // Sweep line along d, starting at 0 int d = 0; int ntask = 0; int sheight = 0; int ei = 0; GECODE_AUTOARRAY(int, prune_tasks, start.size()); int prune_tasks_size = 0; GECODE_AUTOARRAY(int, contribution, start.size()); for (int i = start.size(); i--; ) contribution[i] = 0; d = events[ei].date; while (ei < events_size) { if (events[ei].e != EVENT_PRUN) { if (d != events[ei].date) { GECODE_ES_CHECK(prune(home, d, events[ei].date-1, r, ntask, sheight, contribution, prune_tasks, prune_tasks_size)); d = events[ei].date; } if (events[ei].e == EVENT_CHCK) { ntask += events[ei].inc; } else /* if (it->e == EVENT_PROF) */ { sheight += events[ei].inc; if(events[ei].first_prof) contribution[events[ei].task] = at_most ? std::max(contribution[events[ei].task], events[ei].inc) : std::min(contribution[events[ei].task], events[ei].inc); } } else /* if (it->e == EVENT_PRUN) */ { assert(prune_tasks_size