On Tuesday, 10 July 2012 03:19:52 UTC+8, Robert Bradshaw wrote:
>
> The problem with Graph vs. BipartiteGraph is because the one extends 
> from the other. Instead, both should descend from a common base class 
> that implements the common functionality. Something like add_edge 
> would not belong in this baseclass, and algorithms that take advantage 
> of special graph functionality would override the relevant methods. 
>
> As for the issues of mutability, I think the builder pattern could 
> come in handy here. This could either be explicit or perhaps even 
> cleverly done implicitly with copy-on-write semantics. Properties like 
> bipartiteness could be checked on creation, once, after all edges are 
> added. For example 
>
>     g = 
> GraphBuilder(n).add_edge(...).add_edge(...).asBipartiteGraph(vertex_partition)
>  
>
>
> At the very least a set_immutable() method could be provided to make 
> them hashable and perhaps even transform into an optimized 
> representation. 
>
> Another idea I haven't seen floated is storing the properties of a 
> graph as a set of flags, asserted at creation (or cleared when the 
> graph is modified). 


I mentioned this possibility some time ago, saying that's the way GRAPE 
package in GAP
works.
 

> You could then dynamically look up the best method 
> to use for any algorithm by consulting this flag list (and perhaps 
> some standard naming scheme for automatic lookup). 
>
> and this sounds like GAP's type system :–)
One should give GAP's people a credit here for designing a system 
well-suited
for the quite arcane situations often appearing in algebra.

Dima


 

> - Robert 
>
>
> On Tue, Jun 19, 2012 at 2:06 PM, Nathann Cohen <nathann.co...@gmail.com> 
> wrote: 
> > Hellooooooo everybody !! 
> > 
> > As in the best times, I tried 5 times to answer all who answered this 
> > thread, got angry, erased the message, and began again. But Robert 
> Miller 
> > has a nice saying when we get to such situations : "shut the fuck up and 
> > show me the code". So that's what I will try to do. Give examples 
> instead of 
> > wondering aloud whether the world has gone mad. 
> > 
> > Be warned, for it is a long mail (I --love-- long emails).  It also 
> gives 
> > some idea of what is happening in Sage's graph library these days, if 
> > anybody (but me) cares :-) 
> > 
> > 1) Some history : the BipartiteGraph class. 
> > ----------------------------------------------------------- 
> > The purpose of this class was to *at long last* have Graph classes that 
> > represent graph properties. There are two possibilities when writing 
> such a 
> > thing : 
> > a) make it inherit all the Graph functions 
> > b) make it a new class 
> > The problem when picking b) is that this class gets totally useless as 
> it 
> > has none of the interesting methods of Graph. So a) was picked, and of 
> > course most if not all methods of BipartiteGraph break when called on a 
> > BipartiteGraph object. 
> > The reason why is easy : some of the most fundamental graph operations 
> are 
> > invalid. Like add_edge. 
> > I assure you that not being able to add an edge in a graph is *very* 
> > problematic. 
> > The code ? 
> > 
> > Try the following : 
> > sage: BipartiteGraph(graphs.CompleteBipartiteGraph(3,3)).complement() 
> > 
> > Of course, the complement of a bipartite graph needs not be bipartite, 
> right 
> > ? Yeah, but the graph methods do not know that, nor should they mind 
> most of 
> > the time. Try that : 
> > 
> > sage: 
> BipartiteGraph(graphs.CompleteBipartiteGraph(3,3)).independent_set() 
> > 
> > Basically all graphs functions would need to be rewritten to work with 
> > BipartiteGraph. The only sane design choice would be for BipartiteGraph 
> > *not* to inherit Graph methods, but of course it would be pretty empty 
> and 
> > all methods would need to be copied anyway. 
> > 
> > There is another problem with Bipartite Graph. The add_edge function 
> needs 
> > to be wrapped so that no edge is added to the graph that creates an odd 
> > cycle. This is a costly operation. *VERY* costly operation (compared to 
> > add_edge, of course). And so it was not done this way, for efficiency. 
> Hence 
> > instead of checking whether the graph "plus the new edge" stays 
> bipartite, 
> > the vertices are bicolored from the start, and an exception is raised 
> when 
> > add_edge tries to add an edge that does not fit *the former bicoloring*. 
> > That's mostly incorrect, does not correspond to the user's intent, but 
> it 
> > is....faster >_< 
> > 
> > sage: g = BipartiteGraph(5) 
> > sage: g.add_edge(0,1) 
> > 
> > This could could be fixed by checking that the graph remains bipartite 
> each 
> > time an edge is added, but of course that comes with a cost, and a cost 
> that 
> > it is not healthy to pay when you *just* want to add an edge. The most 
> > natural explanation behind is that you usually know what you are doing 
> when 
> > you add an edge to a graph, and that you should know for yourself 
> whether 
> > you graph remains bipartite or not. If not, please check it with 
> > is_bipartite when you need it, but *do not make everybody's code 10000 
> times 
> > slower out of sheer laziness*. 
> > 
> > At some point somebody proposed to override the complement() function so 
> > that the complement of a graph stays bipartite : this can be achieved by 
> > switching edges and nonedges *between the two sets* and nowhere else. 
> Though 
> > this operation makes perfect sense in graph theory, it would of course 
> be 
> > very dangereous to replace the .complement() function by doing that, for 
> all 
> > algorithms that take a BipartiteGraph as an instance and compute graph 
> > complements would then return wrong result *and not know it*. 
> > 
> > Sooooooo, these are the problems created by a property we want to keep 
> on a 
> > graph. It is not *yet* about immutable graphs, because NONE of these 
> methods 
> > would be available at all. This would also prevent you from running any 
> > method that think they can use the other ones, even when they do not 
> > actually change the graph itself (like independent_set()). 
> > 
> > I said it several times, but for me this class should be removed. It is 
> not 
> > just bad from the computational point of view, but it also creates 
> problems 
> > that new users have no idea how to cope with, because what the methods 
> do is 
> > not what they have in mind. 
> > 
> > 2) Immutable graphs : storing the graph's properties 
> > ----------------------------------------------------------------------- 
> > 
> > Recently in Sage was merged a patch which is an interface with the graph 
> > class database ISGCI [1,2]. There are many "graph classes" (let us 
> > understand this as 'graph property' for the length of this discussion), 
> and 
> > this database can tell us to some extend which properties are implied by 
> > others. When I first wrote this patch, what I had in mind is precisely 
> what 
> > Dima mentionned : some algorithms work for general graphs and have a bad 
> > running time, some other algorithms have a better running time because 
> they 
> > are only meant to be run on graphs with a specific property [3]. 
> > This is tempting, because the graph theory litterature is full of this 
> kind 
> > of algorithms "which work only if my graph has this and this and that". 
> > Hence the goal was to run on each graph the implementation most adapted 
> to 
> > it. 
> > 
> > ~~~~~~ 
> > By the way Dima, there are "very sane ways" to write 
> perfect-graph-specific 
> > algorithms in Graph.py, hence that can be accessed for all graphs. What 
> > would you think of : 
> > Graph.independent_set(algorithm = "perfect_graph") ? 
> > It would not be the default algorithm, it would not be run by mistake, 
> and 
> > because perfect independent set are not only interesting for perfect 
> graphs, 
> > this is actually what would make the most sense. No new class, no 
> overhead 
> > over the most important graph functions... 
> > ~~~~~~ 
> > 
> > Of course, we cannot attempt, each time independent_set is run, to check 
> all 
> > graphs properties for which we have a specific algorithm. Unless the 
> current 
> > implementation (very bad, but -correct-) is changed, it is very costly 
> to 
> > check whether a graph is perfect. Much more than to compute an 
> independent 
> > set, actually. 
> > 
> > Hence it would be nice to have a class of immutable graphs, to which 
> could 
> > be associated (cached) some properties that would not have to be 
> computed 
> > over and over afterwards. The most natural question, now : what about 
> the a) 
> > and b) options from the BipartiteGraph class ? Should they inherit all 
> > methods, or none ? 
> > My personal advice is clear --> absolutely none. Unless somebody can 
> think 
> > of a way to avoid all the problems we had with BipartiteGraph. Hence 
> > immutable graphs would only be used for "storage purposes", and the 
> combinat 
> > guys already (repeatedly) asked for such a thing, as they sometimes need 
> > their graphs to be hashable (and to store them in sets, or as dictionary 
> > keys). It also happened to me recently, and I had to store spar6_string 
> > instead. Not so bad, but not a good answer either. 
> > I also wrote some kind of static graph structure myself -- low level 
> ones -- 
> > because you can perform some optimizations when you know from the start 
> that 
> > the graph will not be modified. Having a static graph structure thus is 
> a 
> > way to have a faster data structure, which we use for instance to 
> compute 
> > distances between all pairs of vertices [4]. 
> > In particular, the purpose of these speedups is to save the time it 
> takes to 
> > call has_edge, or to iterate over the neighbors of a graph. 
> > In particular, it is totally unthinkable that graphs you actually want 
> to 
> > compute things on could be immutable. We already lose time compared to 
> > networkX for stupid things like add/remove edges, there is no way on 
> earth 
> > that add_edge would copy the whole thing. 
> > 
> > Dima, I have been working with Thomas on groups recently, with GAP and 
> > Magma, which was pretty new to me. Of course their objects are usually 
> > immutable, of course you create them once and never touch them, but the 
> way 
> > graph theory works is totally different. We spend our lifetimes adding 
> and 
> > removing edges, and vertices, and building a complete graph on n 
> vertices 
> > edge by edge *cannot* take n^4 time, which is what happens if you copy 
> the 
> > graph each time you want to add an edge. 
> > Honestly. All algorithms are built like that. Any greedy algorithm works 
> > like that, and graph theory has a lot of them. If you want to compute 
> > matchings, if you want to compute decompositions, god, everything is 
> being 
> > done step by step, corrected 10 times, and you just cannot afford to 
> copy 
> > your whole data each time. 
> > 
> > 3) Splitting the files 
> > --------------------------- 
> > 
> > We have something like "three files" for graph methods (data structures 
> > excluded). generic_graph, graph, and digraph. The graph one is pretty 
> thick, 
> > and should be even thicker considering that some methods stored in 
> > generic_graph should be moved to graph.py (old mistakes, most probably 
> > mine). I do not see among the methods any clear way to split them (I 
> already 
> > had to list all graphs methods thematically twice, each time I reached 
> the 
> > same conclusion -- that it is totally arbitrary and unnatural), so I 
> will 
> > refrain from doing it myself. Of course anybody else can write a patch 
> doing 
> > that, which somebody else will review, Sage is a free software so my 
> > personal advice does not matter much. But I do not see how it could be 
> done 
> > smartly. 
> > Some recent additions to Sage's graph library were done in modules. 
> These 
> > functions are not necessarily made available through functions from the 
> > Graph class, but can be imported. 
> > I think that it is the best way to procede now that the algorithms we 
> add 
> > get more and more specific, somehow "research-level", at least in the 
> view I 
> > have of graphs ("topological things", like connectivity, distances, 
> sparse 
> > graphs, that stuff). 
> > 
> > Having a large amount of graph function available in modules only is 
> also a 
> > way to get the users used to query the documentation when they wonder 
> what 
> > Sage can do, and not only the automatic completion of "g.<tab>". Many 
> graph 
> > methods are not that fundamental anyway, and the same way that it would 
> not 
> > make sense to import them systematically to Graph, there is no sense in 
> > creating a "GraphThatYouWantToColor" class whose methods would be the 
> > coloring-related stuff (currently stored in graph_coloring.py). 
> > 
> > Hence, as this thread originally suggested, I think the best is to work 
> with 
> > modules, and to lessen the load of Graph.py by "importing" the useful 
> > functions there, which just requires one line (thank you Florent for 
> your 
> > trick !!!). 
> > I am sorry to only think of the matrix0, matrix1, matrix2 and matrix3 
> files 
> > as a joke. The only reason I see to keep them this way is that "we 
> always 
> > kept them this way", and that people are usually scared of backward 
> > compatibility. Of course, I do not know this code at all, so I will not 
> come 
> > and dirty it with my hands. 
> > Jason suggested that it originally came from a technical problem in 
> Cython, 
> > and that makes sense. Honestly this is just a terrible design, and 
> unless 
> > you are forced to do it, you just don't. That's common sense. You do not 
> use 
> > object-oriented programming to create files matrix0...matrix3. That's 
> > confusing, that's... 
> > Come on, that's just a mistake. 
> > It shows. 
> > 
> > I mean.... The thing is that for me importing stuff from module to 
> classes 
> > just solves everything at no cost. Of course I may have missed things, 
> in 
> > which case somebody will probably tell me what eventually, but why look 
> > anywhere else if we have an answer ? Is there a bad side to this way of 
> > doing ? I work with group theory these days, so I am used to missing 
> obvious 
> > things :-) 
> > 
> > ------------- 
> > 
> > Well. By the academic world's standard this mail is long only forgot 
> > unimportant things, so I better resume filling boxes as I will be 
> leaving 
> > Belgium the day after tomorrow. Gosh, my appartment is a mess ^^; 
> > 
> > Oh, and by the way I expect to be very close to totally unreachable for 
> a 
> > long period, probably two months and a half, from early july to end of 
> > september. If anybody around happens to be in southeast Asia during that 
> > time (Thailand/Vietnam/Cambodge/Laos), I will be backpacking there, 
> let's 
> > meet somewhere !!! 
> > 
> > Haaaaaaaaaaaaaaaaaaaaaaaaaaave 
> > fuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuun ! :-) 
> > 
> > Nathann 
> > 
> > [1] http://www.graphclasses.org/ 
> > [2] http://combinat.sagemath.org/doc/reference/sage/graphs/isgci.html 
> > [3] Of course Dima I only talked about methods *implemented* in Sage 
> > [4] 
> > 
> http://www.sagemath.org/doc/reference/sage/graphs/distances_all_pairs.html 
> > [5] 
> > 
> http://www.sagemath.org/doc/reference/sage/graphs/base/static_sparse_graph.html
>  
> > 
> > -- 
> > To post to this group, send an email to sage-devel@googlegroups.com 
> > To unsubscribe from this group, send an email to 
> > sage-devel+unsubscr...@googlegroups.com 
> > For more options, visit this group at 
> > http://groups.google.com/group/sage-devel 
> > URL: http://www.sagemath.org 
>

-- 
-- 
To post to this group, send an email to sage-devel@googlegroups.com
To unsubscribe from this group, send an email to 
sage-devel+unsubscr...@googlegroups.com
For more options, visit this group at http://groups.google.com/group/sage-devel
URL: http://www.sagemath.org

Reply via email to