// -*- C++ -*- (c) 2007, 2008 Petr Rockai // (c) 2013, 2014 Vladimír Štill #include #include #include #include #include #ifndef DIVINE_ALGORITHM_NDFS_H #define DIVINE_ALGORITHM_NDFS_H namespace divine { namespace algorithm { template< typename Setup > struct NestedDFS : Algorithm, AlgorithmUtils< Setup >, Sequential { typedef NestedDFS< Setup > This; typedef typename Setup::Graph Graph; typedef typename Graph::Node Node; typedef typename Graph::Label Label; typedef typename Setup::Store Store; typedef typename Store::Vertex Vertex; typedef typename Store::Handle Handle; Vertex seed; bool parallel; std::atomic< bool > valid, finished; std::deque< Handle > ce_stack; std::deque< Handle > ce_lasso; std::deque< Handle > toexpand; algorithm::Statistics stats; struct Extension { bool nested() { return _data & _nestedMask; } bool onStack() { return _data & _onStackMask; } void setNested() { _data |= _nestedMask; } void setOnStack() { _data |= _onStackMask; } private: typename Store::template DataWrapper< uint8_t > _data; static constexpr uint8_t _nestedMask = 0x1; static constexpr uint8_t _onStackMask = 0x2; }; Extension &extension( Vertex v ) { return v.template extension< Extension >(); } int id() { return 0; } // expected by AlgorithmUtils inline Pool& pool() { return this->graph().pool(); } void runInner( Graph &graph, Vertex n, Store *store = nullptr ) { seed = n; visitor::DFVReadOnly< Inner > visitor( *this, graph, store ? *store : this->store() ); visitor.exploreFrom( n.node() ); n.disown(); } struct : brick::shmem::Thread { brick::shmem::Fifo< Handle > process; This *outer; std::unique_ptr< Graph > graph; std::unique_ptr< Store > store; void main() { while ( outer->valid.load( std::memory_order_relaxed ) ) { if ( !process.empty() ) { auto n = process.front(); process.pop(); // run the inner loop outer->runInner( *graph, store->vertex( n ), store.get() ); } else { if ( outer->finished && process.empty() ) return; std::this_thread::yield(); } } } } inner; void counterexample() { progress() << "generating counterexample... " << std::flush; typedef LtlCE< Setup, Unit, Unit, Unit > CE; CE ce( this ); auto ceStack = ce.succTraceLocal( *this, typename CE::Linear(), Node(), ce_stack.rbegin(), ce_stack.rend() ); ce.generateLinear( *this, this->graph(), ceStack ); auto ceLasso = ce.succTraceLocal( *this, typename CE::Lasso(), ce.traceBack( ceStack ), ce_lasso.rbegin(), ce_lasso.rend() ); ce.generateLasso( *this, this->graph(), ceLasso ); progress() << "done" << std::endl; result().ceType = meta::Result::CET::Cycle; } void run() { progress() << " searching...\t\t\t" << std::flush; if ( parallel ) { inner.graph->setSlack( this->m_slack ); inner.start(); } visitor::DFV< Outer > visitor( *this, this->graph(), this->store() ); this->graph().initials( this->store().alloc, [&]( Node f, Node t, Label l ) { ASSERT( !this->store().valid( f ) ); visitor.queue( Vertex(), t, l ); } ); visitor.processQueue(); while ( valid.load( std::memory_order_relaxed ) && !toexpand.empty() ) { auto from = this->store().vertex( toexpand.front() ); if ( !this->graph().full( from ) ) this->graph().fullexpand( this->store().alloc, from, [&]( Vertex, Node t, Label ) { visitor::DFV< Outer > visitor( *this, this->graph(), this->store() ); visitor.exploreFrom( t ); } ); toexpand.pop_front(); } finished = true; if ( parallel ) { inner.join(); ASSERT( !valid || inner.process.empty() ); } progress() << "done" << std::endl; if ( !valid ) { if ( parallel ) std::cerr << "Sorry, but counterexamples are not implemented in Parallel Nested DFS." << std::endl << "Please re-run with -w 1 to obtain a counterexample." << std::endl; else counterexample(); } stats.update( meta().statistics ); meta().statistics.deadlocks = -1; /* did not count */ result().propertyHolds = valid ? meta::Result::R::Yes : meta::Result::R::No; result().fullyExplored = valid ? meta::Result::R::Yes : meta::Result::R::No; resultBanner( valid ); } struct Outer : Visit< This, Setup > { static visitor::ExpansionAction expansion( This &dfs, Vertex st ) { if ( !dfs.valid.load( std::memory_order_relaxed ) ) return visitor::ExpansionAction::Terminate; dfs.stats.addNode( dfs.graph(), st ); dfs.ce_stack.push_front( st.handle() ); dfs.extension( st ).setOnStack(); return visitor::ExpansionAction::Expand; } static visitor::TransitionAction transition( This &dfs, Vertex from, Vertex to, Label ) { dfs.stats.addEdge( dfs.store(), from, to ); if ( dfs.store().valid( from ) && !dfs.graph().full( from ) && !dfs.graph().full( to ) && dfs.extension( to ).onStack() ) dfs.toexpand.push_back( from.handle() ); return visitor::TransitionAction::Follow; } static void finished( This &dfs, Vertex n ) { if ( dfs.graph().stateFlags( n.node(), graph::flags::isAccepting ) ) { // run the nested search if ( dfs.parallel ) dfs.inner.process.push( n.handle() ); else dfs.runInner( dfs.graph(), n ); } if ( dfs.valid.load( std::memory_order_relaxed ) && !dfs.ce_stack.empty() ) { ASSERT_EQ( n.handle().asNumber(), dfs.ce_stack.front().asNumber() ); dfs.ce_stack.pop_front(); } } }; struct Inner : Visit< This, Setup > { static visitor::ExpansionAction expansion( This &dfs, Vertex st ) { if ( !dfs.valid.load( std::memory_order_relaxed ) ) return visitor::ExpansionAction::Terminate; dfs.stats.addExpansion(); dfs.ce_lasso.push_front( st.handle() ); return visitor::ExpansionAction::Expand; } static visitor::TransitionAction transition( This &dfs, Vertex from, Vertex to, Label ) { // The search always starts with a transition from "nowhere" into the // initial state. Ignore this transition here. if ( dfs.pool().valid( from.node() ) && to.handle().asNumber() == dfs.seed.handle().asNumber() ) { dfs.valid = false; return visitor::TransitionAction::Terminate; } if ( !dfs.extension( to ).nested() ) { dfs.extension( to ).setNested(); return visitor::TransitionAction::Expand; } return visitor::TransitionAction::Follow; } static void finished( This &dfs, Vertex n ) { if ( !dfs.ce_lasso.empty() ) { ASSERT_EQ( n.handle().asNumber(), dfs.ce_lasso.front().asNumber() ); dfs.ce_lasso.pop_front(); } } }; NestedDFS( Meta m, typename Setup::Generator *g = nullptr ) : Algorithm( m, sizeof( Extension ) ) { valid = true; this->init( *this, g ); parallel = m.execution.threads > 1; if (m.execution.threads > 2) progress() << "WARNING: Nested DFS uses only 2 threads." << std::endl; if ( parallel ) { this->store().setSize( m.execution.initialTable ); // XXX inner.graph = std::unique_ptr< Graph >( this->initGraph( *this ) ); inner.graph->setPool( this->pool() ); // copy the pool (rerefence to lake) inner.store = std::unique_ptr< Store >( new Store( *inner.graph, inner.graph->base().slack(), &this->store() ) ); } finished = false; inner.outer = this; } }; } } #endif