@@ -377,6 +377,232 @@ InstallMethod(DigraphClosure,
377377[ IsImmutableDigraph, IsPosInt] ,
378378{ D, k} -> MakeImmutable(DigraphClosure(DigraphMutableCopy(D), k)));
379379
380+ DIGRAPHS_CheckContractEdgeDigraph := function (D, u, v )
381+ if not IsDigraphEdge(D, u, v) then # Check if [u, v] is an edge in digraph D
382+ ErrorNoReturn(" expected an edge between the 2nd and 3rd arguments " ,
383+ " (vertices) " , u, " and " , v, " but found none" );
384+ elif IsMultiDigraph(D) then
385+ ErrorNoReturn(" The 1st argument (a digraph) must not satisfy " ,
386+ " IsMultiDigraph" );
387+ elif u = v then # edge should not be contracted if u = v
388+ ErrorNoReturn(" The 2nd argument <u> must not be equal to the 3rd " ,
389+ " argument <v>" );
390+ fi ;
391+
392+ end ;
393+
394+ InstallMethod(DigraphContractEdge,
395+ " for a mutable digraph and two positive integers" ,
396+ [ IsMutableDigraph, IsPosInt, IsPosInt] ,
397+ function (D, u, v )
398+ local w, neighbours, transformations_to_edge, transformations_to_old_edges,
399+ old_edge, new_edge, neighbour, t, vertices, edge_label;
400+
401+ DIGRAPHS_CheckContractEdgeDigraph(D, u, v);
402+
403+ # if (v, u) is an edge, disallow loops, so remove (v, u)
404+ if IsDigraphEdge(D, v, u) then
405+ DigraphRemoveEdge(D, v, u);
406+ fi ;
407+
408+ # remove the contracted edge
409+ DigraphRemoveEdge(D, u, v);
410+
411+ # Find vertex neighbours of u and v to construct new incident edges of w
412+
413+ neighbours := [ Immutable(InNeighboursOfVertex(D, u)),
414+ Immutable(InNeighboursOfVertex(D, v)),
415+ Immutable(OutNeighboursOfVertex(D, u)),
416+ Immutable(OutNeighboursOfVertex(D, v))] ;
417+
418+ # immutable reference to old edge labels to add to any
419+ # new transformed incident edges
420+
421+ DigraphAddVertex(D, [ DigraphVertexLabel(D, u),
422+ DigraphVertexLabel(D, v)] ); # add vertex w
423+
424+ vertices := DigraphVertices(D);
425+ w := Length(vertices); # w is the new vertex identifier
426+
427+ # Handle loops from edges u or w, with the same source / range
428+ # No relevant edges are removed from D until vertices are removed
429+ # So old edge labls are taken from D
430+
431+ if IsDigraphEdge(D, u, u) and IsDigraphEdge(D, v, v) then
432+ DigraphAddEdge(D, w, w);
433+ edge_label := [ DigraphEdgeLabel(D, u, u), DigraphEdgeLabel(D, w, w)] ;
434+ SetDigraphEdgeLabel(D, w, w, edge_label);
435+ elif IsDigraphEdge(D, u, u) then
436+ DigraphAddEdge(D, w, w);
437+ edge_label := DigraphEdgeLabel(D, u, u);
438+ SetDigraphEdgeLabel(D, w, w, edge_label);
439+ elif IsDigraphEdge(D, v, v) then
440+ DigraphAddEdge(D, w, w);
441+ edge_label := DigraphEdgeLabel(D, v, v);
442+ SetDigraphEdgeLabel(D, w, w, edge_label);
443+ fi ;
444+
445+ # translate edges based on neighbours
446+
447+ # transformation functions translating neighbours to their new edge
448+ transformations_to_edge := [{ x, y} -> [ x, y] ,
449+ { x, y} -> [ x, y] ,
450+ { x, y} -> [ y, x] ,
451+ { x, y} -> [ y, x]] ;
452+
453+ # transformation functions translating neighbours
454+ # to their original edge in D
455+
456+ transformations_to_old_edges := [ e -> [ e, u] ,
457+ e -> [ e, v] ,
458+ e -> [ u, e] ,
459+ e -> [ v, e]] ;
460+
461+ # Add translated new edges, and setup edge labels
462+
463+ for t in [ 1 .. Length(transformations_to_edge)] do
464+ for neighbour in neighbours[ t] do
465+ new_edge := transformations_to_edge[ t] (neighbour, w);
466+ old_edge := transformations_to_old_edges[ t] (neighbour);
467+ edge_label := DigraphEdgeLabel(D, old_edge[ 1 ] , old_edge[ 2 ] );
468+ DigraphAddEdge(D, new_edge);
469+ SetDigraphEdgeLabel(D, new_edge[ 1 ] , new_edge[ 2 ] , edge_label);
470+ od ;
471+ od ;
472+
473+ DigraphRemoveVertices(D, [ u, v] ); # remove the vertices of the
474+ # contracted edge (and therefore,
475+ # all its incident edges)
476+ return D;
477+ end );
478+
479+ InstallMethod(DigraphContractEdge,
480+ " for an immutable digraph and two positive integers" ,
481+ [ IsImmutableDigraph, IsPosInt, IsPosInt] ,
482+ function (D, u, v )
483+ local w, neighbours, transformations_to_edge, transformations_to_old_edges,
484+ existing_edges, edges_to_not_include, new_digraph, new_edge, old_edge,
485+ neighbour, t, vertices, edge_label;
486+
487+ DIGRAPHS_CheckContractEdgeDigraph(D, u, v);
488+
489+ # Incident edges to [u, v] that will be transformed to be incident to w
490+ edges_to_not_include := [] ;
491+
492+ existing_edges := [] ; # existing edges that should be re added
493+
494+ # contracted edge should not be added to the new digraph
495+ new_digraph := NullDigraph(IsMutableDigraph, 0 );
496+
497+ # if (v, u) is an edge, disallow loops, so remove (v, u)
498+ if IsDigraphEdge(D, v, u) then
499+ D := DigraphRemoveEdge(D, v, u);
500+ fi ;
501+
502+ D := DigraphRemoveEdge(D, u, v); # remove the edge to be contracted
503+
504+ DigraphAddVertices(new_digraph, DigraphVertexLabels(D));
505+
506+ # add vertex w with combined labels from u and v
507+ DigraphAddVertex(new_digraph, [ DigraphVertexLabel(D, u),
508+ DigraphVertexLabel(D, v)] ); # add vertex w
509+
510+ vertices := DigraphVertices(new_digraph);
511+ w := Length(vertices); # w is the new vertex identifier
512+
513+ # Handle loops from edges u or w, with the same source / range
514+
515+ if IsDigraphEdge(D, u, u) and IsDigraphEdge(D, v, v) then
516+ DigraphAddEdge(new_digraph, w, w);
517+ SetDigraphEdgeLabel(new_digraph, w, w, [ DigraphEdgeLabel(D, u, u),
518+ DigraphEdgeLabel(D, v, v)] );
519+ elif IsDigraphEdge(D, u, u) then
520+ DigraphAddEdge(new_digraph, w, w);
521+ SetDigraphEdgeLabel(new_digraph, w, w,
522+ DigraphEdgeLabel(D, u, u));
523+ elif IsDigraphEdge(D, v, v) then
524+ DigraphAddEdge(new_digraph, w, w);
525+ SetDigraphEdgeLabel(new_digraph, w, w, DigraphEdgeLabel(D, v, v));
526+ fi ;
527+
528+ # if there were loops in D at the vertices u or w, don't include these edges,
529+ # but include a new loop at w
530+
531+ Append(edges_to_not_include, [[ u, u] , [ v, v]] );
532+
533+ # Find vertex neighbours of u and v to construct new incident edges of w
534+
535+ neighbours := [ InNeighboursOfVertex(D, u),
536+ InNeighboursOfVertex(D, v),
537+ OutNeighboursOfVertex(D, u),
538+ OutNeighboursOfVertex(D, v)] ;
539+
540+ # translate edges based on neighbours
541+
542+ # transformation functions translating neighbours to their new edge
543+
544+ transformations_to_edge := [{ x, y} -> [ x, y] , { x, y} -> [ x, y] ,
545+ { x, y} -> [ y, x] , { x, y} -> [ y, x]] ;
546+
547+ # transformation functions translating neighbours to their original edge in D
548+
549+ transformations_to_old_edges := [ e -> [ e, u] ,
550+ e -> [ e, v] ,
551+ e -> [ u, e] ,
552+ e -> [ v, e]] ;
553+
554+ # remove edges that will be adjusted
555+
556+ for t in [ 1 .. Length(transformations_to_old_edges)] do
557+ Append(edges_to_not_include, List(neighbours[ t] ,
558+ transformations_to_old_edges[ t] ));
559+ od ;
560+
561+ # Find edges that should be included, but remain the same
562+
563+ Sort(edges_to_not_include);
564+ Append(existing_edges, ShallowCopy(DigraphEdges(D)));
565+ Sort(existing_edges);
566+ SubtractSet(existing_edges, edges_to_not_include);
567+
568+ # Add translated new edges, and setup edge labels
569+
570+ for t in [ 1 .. Length(transformations_to_edge)] do
571+ for neighbour in neighbours[ t] do
572+ new_edge := transformations_to_edge[ t] (neighbour, w);
573+
574+ # old_edge is what the new transformed edge was previously
575+ old_edge := transformations_to_old_edges[ t] (neighbour);
576+ edge_label := DigraphEdgeLabel(D, old_edge[ 1 ] , old_edge[ 2 ] );
577+ new_digraph := DigraphAddEdge(new_digraph, new_edge);
578+ SetDigraphEdgeLabel(new_digraph, new_edge[ 1 ] , new_edge[ 2 ] , edge_label);
579+ od ;
580+ od ;
581+
582+ # Add the existing edges that have not changed,
583+ # and set their edge labels to that of the previous digraph
584+
585+ for new_edge in existing_edges do
586+ new_digraph := DigraphAddEdge(new_digraph, new_edge);
587+ SetDigraphEdgeLabel(new_digraph, new_edge[ 1 ] , new_edge[ 2 ] ,
588+ DigraphEdgeLabel(D, new_edge[ 1 ] , new_edge[ 2 ] ));
589+ od ;
590+
591+ # remove the vertices of the contracted edge
592+ return MakeImmutable(DigraphRemoveVertices(new_digraph, [ u, v] ));
593+
594+ end );
595+
596+ InstallMethod(DigraphContractEdge,
597+ " for a digraph and a dense list" ,
598+ [ IsDigraph, IsDenseList] ,
599+ function (D, edge )
600+ if Length(edge) <> 2 then
601+ ErrorNoReturn(" the 2nd argument <edge> must be a list of length 2" );
602+ fi ;
603+ return DigraphContractEdge(D, edge[ 1 ] , edge[ 2 ] );
604+ end );
605+
380606# ############################################################################
381607# 3. Ways of combining digraphs
382608# ############################################################################
0 commit comments