// -*- C++ -*- (c) 2007, 2008 Petr Rockai #include #include #ifndef DIVINE_ALGORITHM_METRICS_H #define DIVINE_ALGORITHM_METRICS_H namespace divine { namespace algorithm { /** * A generalized statistics tracker for parallel algorithms. Put an instance in * your Shared structure and use the addNode/addEdge traps in your algorithm to * collect data. Use merge() at the end of your computation to combine the data * from multiple parallel workers. Call updateResult to popuplate your Result * structure with collected data (for the report). */ struct Statistics { int64_t states, transitions, accepting, deadlocks, expansions; Statistics() : states( 0 ), transitions( 0 ), accepting( 0 ), deadlocks( 0 ), expansions( 0 ) {} template< typename Graph, typename Vertex > void addNode( Graph &g, const Vertex& n ) { ++states; if ( g.stateFlags( n.node(), graph::flags::isAccepting ) ) ++ accepting; addExpansion(); } void addExpansion() { ++expansions; } template< typename Store > void addEdge( Store &s, const typename Store::Vertex& n, const typename Store::Vertex& ) { if ( s.valid( n ) ) ++ transitions; } void addDeadlock() { ++ deadlocks; } void update( meta::Statistics &s ) { s.visited += states; s.deadlocks += deadlocks; s.expanded += expansions; s.transitions += transitions; s.accepting += accepting; } void merge( Statistics other ) { states += other.states; transitions += other.transitions; accepting += other.accepting; expansions += other.expansions; deadlocks += other.deadlocks; } }; template< typename BS > typename BS::bitstream &operator<<( BS &bs, Statistics st ) { return bs << st.states << st.transitions << st.expansions << st.accepting << st.deadlocks; } template< typename BS > typename BS::bitstream &operator>>( BS &bs, Statistics &st ) { return bs >> st.states >> st.transitions >> st.expansions >> st.accepting >> st.deadlocks; } struct MetricsShared : algorithm::Statistics { bool need_expand; MetricsShared() : need_expand( false ) {} }; /** * A very simple state space measurement algorithm. Explores the full state * space, keeping simple numeric statistics (see the Statistics template * above). */ template< typename Setup > struct Metrics : Algorithm, AlgorithmUtils< Setup, MetricsShared >, Parallel< Setup::template Topology, Metrics< Setup > > { using This = Metrics< Setup >; using Shared = MetricsShared; using Utils = AlgorithmUtils< Setup, Shared >; ALGORITHM_CLASS( Setup ); BRICK_RPC( Utils, &This::_visit, &This::_por, &This::_por_worker ); struct Main : Visit< This, Setup > { static visitor::ExpansionAction expansion( This &t, const Vertex &st ) { t.shared.addNode( t.graph(), st ); return visitor::ExpansionAction::Expand; } static visitor::TransitionAction transition( This &t, Vertex from, Vertex to, Label ) { t.shared.addEdge( t.store(), from, to ); t.graph().porTransition( t.store(), from, to ); return visitor::TransitionAction::Follow; } static visitor::DeadlockAction deadlocked( This &t, Vertex ) { t.shared.addDeadlock(); return visitor::DeadlockAction::Ignore; } }; void _visit() { // parallel this->visit( this, Main(), this->shared.need_expand ); } void _por_worker() { this->graph()._porEliminate( *this ); } Shared _por( Shared sh ) { this->shared = sh; if ( this->graph().porEliminate( *this, *this ) ) this->shared.need_expand = true; return this->shared; } Metrics( Meta m ) : Algorithm( m, 0 ) { this->init( *this ); } Metrics( Metrics &master, std::pair< int, int > id ) : Algorithm( master.meta(), 0 ) { this->init( *this, master, id ); } void banner( std::ostream &o ) { auto &s = meta().statistics; o << " ============================================= " << std::endl << std::setw( 12 ) << s.visited << " states" << std::endl << std::setw( 12 ) << s.transitions << " transitions" << std::endl << std::setw( 12 ) << s.accepting << " accepting" << std::endl << std::setw( 12 ) << s.deadlocks << " deadlocks " << std::endl << " ============================================= " << std::endl; } void collect() { for ( auto s : this->shareds ) s.update( meta().statistics ); } void run() { progress() << " exploring... \t\t\t " << std::flush; parallel( &This::_visit ); collect(); do { this->shared.need_expand = false; ring( &This::_por ); if ( this->shared.need_expand ) { parallel( &This::_visit ); collect(); } } while ( this->shared.need_expand ); progress() << "done" << std::endl; banner( progress() ); result().fullyExplored = meta::Result::R::Yes; this->shared.update( meta().statistics ); } }; } } #endif