Boost logo

Boost :

Subject: Re: [boost] [SoC] BGL project idea
From: Eric Wolf (eric_at_[hidden])
Date: 2010-07-14 00:48:23


Jeremiah Willcock <jewillco_at_[hidden]> writes:

Dear Jeremiah,

>>> I looked through your tarball. Here are some comments:
>>>
>>> Why are you computing the transitive closure explicitly when it is not
>>> part of the algorithm's documented output? It there a reason that you
>>> can't use the SCC structure (and the connections between them)
>>> directly to compute the reduction without storing the full closure?
>>
>> You mean the edge_in_closure adjacency matrix in
>> transitive_reduction_for_dag? It's part of the algorithm to have the
>> reflexive transitive closure stored. I have no idea how I could generate
>> the same behavior in the decision logic ( the "if( not
>> edge_in_closure[i][*it] )" line ) without it.
>
> I guess you could do a BFS, but if the traditional algorithm uses an
> explicit closure you probably should too. You might want to mention
> the quadratic memory usage though. It would be better to have an
> explicit adjacency_matrix graph for the closure. Is there a reason
> you're not using the current transitive_closure algorithm in BGL?

I will modify transitive_closure from Vladimir Prus and use
successor sets. This will give better time complexity and better
memory usage.

>>> Why are there functions that do the transitive closure as well as the
>>> reduction? Is having both of them a common use case? It appears that
>>> there is a lot of redundant code between the reduction-with-closure
>>> and reduction-only functions.
>>
>> It's the same algorithm. The algorithm for computing the closure and the
>> reduction is essentially the same. So you could have the other one for
>> free if you generate one of them. I doubt that it is a common use case,
>> I admit, but I thought one should have the possibility to get both in
>> one run, if its for free. So the reduction-with-closure and
>> reduction-only functions are essentially duplicates of each other.
>
> OK. Is there a way to factor out the common code?

** long lines below **

Several ways to accomplish that come to my mind, but I'm unsure which
route to go and ask you for some advice.

1) Define a Graph type which absorbs all add_vertex and add_edge
operations like /dev/null in unix, then write one implementation
function like

namespace detail {
template < typename Graph,
         typename GraphTC,
         typename G_to_TC_map,
         typename GraphTR,
         typename G_to_TR_map,
         typename VertexIndexMap >
void transitive_reduction_and_closure(
     Graph g, GraphTC tc, G_to_TC_map g_to_tc_map,
     GraphTR tr, G_to_TR_map g_to_tr_map,
     VertexIndexMap vertex_index_map)
{
\\impl
}
}

and two interface functions

template < typename Graph,
         typename GraphTC,
         typename G_to_TC_map,
         typename VertexIndexMap >
void transitive_closure(
     Graph g, GraphTC tc, G_to_TC_map g_to_tc_map,
     VertexIndexMap vertex_index_map)
{
        detail::absorbing_graph ag;
        transitive_reduction_and_closure( g, tc, g_to_tc_map,
        ag, identity_property_map() );
}

transitive_reduction is analog.

Disadvantages: strange type with empty functions needs
to be thrown away by compiler optimization and its not clear
to me if such a type would meet the requirements of the
Graph concept as it clearly states that add_edge should return
a vertex_descriptor for the inserted vertex. Returning
null_vertex() as no vertex got inserted is splitting hairs
and not in the purpose of the Graph concept?

2) Use boost::enable_if like that: (In the following ... denotes an
ellipsis of zero or more parameters, not varargs.)

namespace detail {

          struct DONT_COMPUTE_TR {};
          struct DONT_COMPUTE_TC {};

          template < ..., GraphTC >
          void construct_tc_from_successor_set( ..., GraphTC tc, ...
          boost::enable_if< boost::is_same< GraphTC,
                            detail::DONT_COMPUTE_TC >::type * = 0 )
          {
            //dont do anything, as we shall not construct an TC
          }

          template < ..., GraphTC >
          void construct_tc_from_successor_set( ..., GraphTC tc, ...
          boost::disable_if< boost::is_same< GraphTC,
                             detail::DONT_COMPUTE_TC >::type * = 0 )
          {
            ...
            //compute the transitive_closure and build it up in tc
          }

          //transitive_reduction is analog
          
          template < typename Graph,
                   typename GraphTC,
                   typename G_to_TC_map,
                   typename GraphTR,
                   typename G_to_TR_map,
                   typename VertexIndexMap >
          void transitive_reduction_and_closure(
               Graph g, GraphTC tc, G_to_TC_map g_to_tc_map,
               GraphTR tr, G_to_TR_map g_to_tr_map,
               VertexIndexMap vertex_index_map)
         {
               \\impl

               construct_tc_from_successor_set( ... );
               construct_tr( ... );
         }
}

template < typename Graph,
         typename GraphTC,
         typename G_to_TC_map,
         typename VertexIndexMap >
void transitive_closure(
     Graph g, GraphTC tc, G_to_TC_map g_to_tc_map,
     VertexIndexMap vertex_index_map)
{
        detail::transitive_reduction_and_closure( g, tc,
               g_to_tc_map, detail::DONT_COMPUTE_TR(),
        identity_property_map() );
}

transitive_reduction is analog.

advantages: - unused code is not compiled into the executable as SFINAE
prevents this

disadvantages: - pulls in boost/utility/enable_if.hpp and
boost/type_traits/is_same.hpp
               - the transitive reduction cannot be computed from the
                 successor set, so there would be a builder for
                 the transitive reduction of the _condensation
                 graph_, which I would implemet with a template class
                 and a template class specializiation with enable_if
                 like above.

3) a suggestion from you?

As I don't know your suggestion yet, at the moment I vote for 2) or
something similar. Is there a “template pattern“ which I could
use? To me 2) appears a little bit naive but could it work?

Yours,

Eric


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk