Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  digraph.gi   Sprache: unbekannt

 
#############################################################################
##
##  digraph.gi
##  Copyright (C) 2014-21                                James D. Mitchell
##
##  Licensing information can be found in the README file of this package.
##
#############################################################################
##

########################################################################
# This file is organised as follows:
#
# 1.  Digraph types
# 2.  Digraph no-check constructors
# 3.  Digraph copies
# 4.  PostMakeImmutable
# 5.  Digraph constructors
# 6.  Printing, viewing, strings
# 7.  Operators
# 8.  Digraph by-something constructors
# 9.  Converters to/from other types -> digraph
# 10. Random digraphs
#
########################################################################

BindGlobal("DIGRAPHS_NamedDigraphs", fail);
BindGlobal("DIGRAPHS_NamedDigraphsTests", fail);

BindGlobal("DIGRAPHS_LoadNamedDigraphs", function()
  # Check if the database has already been loaded
  if DIGRAPHS_NamedDigraphs = fail then
    MakeReadWriteGlobal("DIGRAPHS_NamedDigraphs");
    UnbindGlobal("DIGRAPHS_NamedDigraphs");

    # Initialise empty record
    BindGlobal("DIGRAPHS_NamedDigraphs", rec());

    # Populate record with entries from the named digraphs main database
    Read(Concatenation(DIGRAPHS_Dir(), "/data/named-digraphs-main-database.g"));
  fi;
end);

BindGlobal("DIGRAPHS_LoadNamedDigraphsTests", function()
  # INTENDED ONLY FOR TESTING PURPOSES
  # Check if the database has already been loaded
  if DIGRAPHS_NamedDigraphsTests = fail then
    MakeReadWriteGlobal("DIGRAPHS_NamedDigraphsTests");
    UnbindGlobal("DIGRAPHS_NamedDigraphsTests");

    # Initialise empty record
    BindGlobal("DIGRAPHS_NamedDigraphsTests", rec());

    # Populate record with entries from the named digraphs test database
    Read(Concatenation(DIGRAPHS_Dir(), "/data/named-digraphs-test-database.g"));
  fi;
end);

InstallMethod(DigraphMutabilityFilter, "for a digraph", [IsDigraph],
function(D)
  if IsMutableDigraph(D) then
    return IsMutableDigraph;
  else
    return IsImmutableDigraph;
  fi;
end);

########################################################################
# 1. Digraph types
########################################################################

BindGlobal("DigraphByOutNeighboursType", NewType(DigraphFamily,
                                         IsDigraphByOutNeighboursRep));

########################################################################
# 2. Digraph no-check constructors
########################################################################

InstallMethod(ConvertToMutableDigraphNC, "for a record", [IsRecord],
function(record)
  local D;
  Assert(1, IsBound(record.OutNeighbours));
  Assert(1, Length(NamesOfComponents(record)) = 1);
  D := Objectify(DigraphByOutNeighboursType, record);
  SetFilterObj(D, IsMutable);
  return D;
end);

InstallMethod(ConvertToMutableDigraphNC, "for a dense list of out-neighbours",
[IsDenseList],
function(list)
  local record;
  record := rec(OutNeighbours := list);
  Perform(record.OutNeighbours, IsSet);
  return ConvertToMutableDigraphNC(record);
end);

InstallMethod(ConvertToImmutableDigraphNC, "for a record", [IsRecord],
record -> MakeImmutable(ConvertToMutableDigraphNC(record)));

InstallMethod(ConvertToImmutableDigraphNC,
"for a dense list of out-neighbours",
[IsDenseList],
list -> MakeImmutable(ConvertToMutableDigraphNC(list)));

InstallMethod(DigraphConsNC, "for IsMutableDigraph and a record",
[IsMutableDigraph, IsRecord],
function(_, record)
  local required, out, name;
  required := ["DigraphRange", "DigraphSource", "DigraphNrVertices"];
  for name in required do
    Assert(1, IsBound(record.(name)));
  od;
  for name in Difference(RecNames(record), required) do
    Info(InfoWarning, 1, "ignoring record component \"", name, "\"!");
  od;
  out := DIGRAPH_OUT_NEIGHBOURS_FROM_SOURCE_RANGE(record.DigraphNrVertices,
                                                  record.DigraphSource,
                                                  record.DigraphRange);
  return ConvertToMutableDigraphNC(out);
end);

InstallMethod(DigraphConsNC,
"for IsMutableDigraph and a dense list of out-neighbours",
[IsMutableDigraph, IsDenseList],
function(_, list)
  Assert(1, not IsMutable(list));
  return ConvertToMutableDigraphNC(List(list, ShallowCopy));
end);

InstallMethod(DigraphConsNC,
"for IsMutableDigraph and a dense mutable list of out-neighbours",
[IsMutableDigraph, IsDenseList and IsMutable],
{_, list} -> ConvertToMutableDigraphNC(StructuralCopy(list)));

InstallMethod(DigraphConsNC, "for IsImmutableDigraph and a record",
[IsImmutableDigraph, IsRecord],
function(_, record)
  local D;
  D := MakeImmutable(DigraphConsNC(IsMutableDigraph, record));
  SetDigraphSource(D, StructuralCopy(record.DigraphSource));
  SetDigraphRange(D, StructuralCopy(record.DigraphRange));
  return D;
end);

InstallMethod(DigraphConsNC, "for IsImmutableDigraph and a dense list",
[IsImmutableDigraph, IsDenseList],
{_, list} -> MakeImmutable(DigraphConsNC(IsMutableDigraph, list)));

InstallMethod(DigraphNC, "for a function and a dense list",
[IsFunction, IsDenseList], DigraphConsNC);

InstallMethod(DigraphNC, "for a dense list", [IsDenseList],
list -> DigraphConsNC(IsImmutableDigraph, list));

InstallMethod(DigraphNC, "for a function and a record",
[IsFunction, IsRecord], DigraphConsNC);

InstallMethod(DigraphNC, "for a record", [IsRecord],
record -> DigraphConsNC(IsImmutableDigraph, record));

########################################################################
# 3. Digraph copies
########################################################################

InstallMethod(DigraphMutableCopy, "for a digraph by out-neighbours",
[IsDigraphByOutNeighboursRep],
function(D)
  local copy;
  copy := ConvertToMutableDigraphNC(OutNeighboursMutableCopy(D));
  SetDigraphVertexLabels(copy, StructuralCopy(DigraphVertexLabels(D)));
  if HaveEdgeLabelsBeenAssigned(D) then
    SetDigraphEdgeLabelsNC(copy, StructuralCopy(DigraphEdgeLabelsNC(D)));
  fi;
  return copy;
end);

InstallMethod(DigraphImmutableCopy, "for a digraph by out-neighbours",
[IsDigraphByOutNeighboursRep],
function(D)
  local copy;
  copy := ConvertToImmutableDigraphNC(OutNeighboursMutableCopy(D));
  SetDigraphVertexLabels(copy, StructuralCopy(DigraphVertexLabels(D)));
  if HaveEdgeLabelsBeenAssigned(D) then
    SetDigraphEdgeLabelsNC(copy, StructuralCopy(DigraphEdgeLabelsNC(D)));
  fi;
  return copy;
end);

InstallMethod(DigraphCopySameMutability, "for a mutable digraph",
[IsMutableDigraph], DigraphMutableCopy);

InstallMethod(DigraphCopySameMutability, "for an immutable digraph",
[IsImmutableDigraph], DigraphImmutableCopy);

InstallMethod(DigraphImmutableCopyIfMutable, "for a mutable digraph",
[IsMutableDigraph], DigraphImmutableCopy);

InstallMethod(DigraphImmutableCopyIfMutable, "for an immutable digraph",
[IsImmutableDigraph], IdFunc);

InstallMethod(DigraphImmutableCopyIfImmutable, "for a mutable digraph",
[IsMutableDigraph], IdFunc);

InstallMethod(DigraphImmutableCopyIfImmutable, "for an immutable digraph",
[IsImmutableDigraph], DigraphImmutableCopy);

InstallMethod(DigraphMutableCopyIfImmutable, "for a mutable digraph",
[IsMutableDigraph], IdFunc);

InstallMethod(DigraphMutableCopyIfImmutable, "for an immutable digraph",
[IsImmutableDigraph], DigraphMutableCopy);

InstallMethod(DigraphMutableCopyIfMutable, "for a mutable digraph",
[IsMutableDigraph], DigraphMutableCopy);

InstallMethod(DigraphMutableCopyIfMutable, "for an immutable digraph",
[IsImmutableDigraph], IdFunc);

########################################################################
# 4. PostMakeImmutable
########################################################################

InstallMethod(PostMakeImmutable, "for a digraph",
[IsDigraph and IsDigraphByOutNeighboursRep],
function(D)
  MakeImmutable(D!.OutNeighbours);
  SetFilterObj(D, IsImmutableDigraph);
  SetFilterObj(D, IsAttributeStoringRep);
end);

########################################################################
# 5. Digraph constructors
########################################################################

InstallMethod(DigraphCons, "for IsMutableDigraph and a record",
[IsMutableDigraph, IsRecord],
function(_, record)
  local D, cmp, labels, i;

  if IsGraph(record) then
    # IsGraph is a function not a filter, so we cannot have a separate method
    D := DigraphNC(IsMutableDigraph,
                   List(Vertices(record), x -> Adjacency(record, x)));
    if IsBound(record.names) then
      SetDigraphVertexLabels(D, StructuralCopy(record.names));
    fi;
    return D;
  fi;

  if not (IsBound(record.DigraphSource)
          and IsBound(record.DigraphRange)
          and (IsBound(record.DigraphVertices) or
               IsBound(record.DigraphNrVertices))) then
    ErrorNoReturn("the argument <record> must be a record with components ",
                  "'DigraphSource', 'DigraphRange', and either ",
                  "'DigraphVertices' or 'DigraphNrVertices' (but not both),");
  elif not IsList(record.DigraphSource)
      or not IsList(record.DigraphRange) then
    ErrorNoReturn("the record components 'DigraphSource' and 'DigraphRange' ",
                  "must be lists,");
  elif Length(record.DigraphSource) <> Length(record.DigraphRange) then
    ErrorNoReturn("the record components 'DigraphSource' and 'DigraphRange' ",
                  "must have equal length,");
  elif IsBound(record.DigraphVertices)
      and IsBound(record.DigraphNrVertices) then
    ErrorNoReturn("the record must only have one of the components ",
                  "'DigraphVertices' and 'DigraphNrVertices', not both,");
  fi;

  if IsBound(record.DigraphNrVertices) then
    if (not IsInt(record.DigraphNrVertices))
        or record.DigraphNrVertices < 0 then
      ErrorNoReturn("the record component 'DigraphNrVertices' ",
                    "must be a non-negative integer,");
    fi;
    cmp := x -> IsPosInt(x) and x < record.DigraphNrVertices + 1;
  else
    Assert(1, IsBound(record.DigraphVertices));
    if not IsList(record.DigraphVertices) then
      ErrorNoReturn("the record component 'DigraphVertices' must be a list,");
    elif not IsDuplicateFreeList(record.DigraphVertices) then
      ErrorNoReturn("the record component 'DigraphVertices' must be ",
                    "duplicate-free,");
    fi;
    cmp := x -> x in record.DigraphVertices;
    record.DigraphNrVertices := Length(record.DigraphVertices);
  fi;

  if not ForAll(record.DigraphSource, cmp) then
    ErrorNoReturn("the record component 'DigraphSource' is invalid,");
  elif not ForAll(record.DigraphRange, cmp) then
    ErrorNoReturn("the record component 'DigraphRange' is invalid,");
  fi;

  record := StructuralCopy(record);

  # Rewrite the vertices to numbers
  if IsBound(record.DigraphVertices) then
    if record.DigraphVertices <> [1 .. record.DigraphNrVertices] then
      for i in [1 .. Length(record.DigraphSource)] do
        record.DigraphRange[i]  := Position(record.DigraphVertices,
                                            record.DigraphRange[i]);
        record.DigraphSource[i] := Position(record.DigraphVertices,
                                            record.DigraphSource[i]);
      od;
      labels := record.DigraphVertices;
      Unbind(record.DigraphVertices);
    fi;
  fi;

  record.DigraphRange := Permuted(record.DigraphRange,
                                  Sortex(record.DigraphSource));
  D := DigraphNC(IsMutableDigraph, record);
  if IsBound(labels) then
    SetDigraphVertexLabels(D, labels);
  fi;
  return D;
end);

InstallMethod(DigraphCons,
"for IsMutableDigraph and a dense list of out-neighbours",
[IsMutableDigraph, IsDenseList],
function(_, list)
  local sublist, v;
  for sublist in list do
    if not IsHomogeneousList(sublist) then
      ErrorNoReturn("the argument <list> must be a list of lists of positive ",
                    "integers not exceeding the length of the argument,");
    fi;
    for v in sublist do
      if not IsPosInt(v) or v > Length(list) then
        ErrorNoReturn("the argument <list> must be a list of lists of ",
                      "positive integers not exceeding the length of ",
                      "the argument,");
      fi;
    od;
  od;
  return DigraphNC(IsMutableDigraph, list);
end);

InstallMethod(DigraphCons, "for IsMutableDigraph, a list, and function",
[IsMutableDigraph, IsList, IsFunction],
function(_, list, func)
  local N, out, x, i, j;
  N    := Size(list);  # number of vertices
  out := List([1 .. N], x -> []);
  for i in [1 .. N] do
    x := list[i];
    for j in [1 .. N] do
      if func(x, list[j]) then
        Add(out[i], j);
      fi;
    od;
  od;
  return ConvertToMutableDigraphNC(out);
end);

InstallMethod(DigraphCons,
"for IsMutableDigraph, a number of vertices, source, and range",
[IsMutableDigraph, IsInt, IsList, IsList],
function(_, N, src, ran)
  return DigraphCons(IsMutableDigraph, rec(DigraphNrVertices := N,
                                           DigraphSource     := src,
                                           DigraphRange      := ran));
end);

InstallMethod(DigraphCons,
"for IsMutableDigraph, a list of vertices, source, and range",
[IsMutableDigraph, IsList, IsList, IsList],
function(_, domain, src, ran)
  local D;
  D := DigraphCons(IsMutableDigraph, rec(DigraphVertices := domain,
                                         DigraphSource   := src,
                                         DigraphRange    := ran));
  SetDigraphVertexLabels(D, domain);
  return D;
end);

InstallMethod(DigraphCons, "for IsImmutableDigraph and a record",
[IsImmutableDigraph, IsRecord],
function(_, record)
  local D;
  D := MakeImmutable(DigraphCons(IsMutableDigraph, record));
  if IsGraph(record) then
    # IsGraph is a function not a filter, so we cannot have a separate method
    # for this.
    if not IsTrivial(record.group) then
      Assert(1, IsPermGroup(record.group));
      SetDigraphGroup(D, record.group);
      SetDigraphSchreierVector(D, record.schreierVector);
      SetRepresentativeOutNeighbours(D, record.adjacencies);
    fi;
  else
    SetDigraphNrEdges(D, Length(record.DigraphSource));
  fi;
  return D;
end);

InstallMethod(DigraphCons,
"for IsImmutableDigraph and a dense list of out-neighbours",
[IsImmutableDigraph, IsDenseList],
{_, list} -> MakeImmutable(DigraphCons(IsMutableDigraph, list)));

InstallMethod(DigraphCons, "for IsImmutableDigraph, a list, and function",
[IsImmutableDigraph, IsList, IsFunction],
function(_, list, func)
  local D;
  D := MakeImmutable(DigraphCons(IsMutableDigraph, list, func));
  SetDigraphAdjacencyFunction(D, {u, v} -> func(list[u], list[v]));
  SetFilterObj(D, IsDigraphWithAdjacencyFunction);
  SetDigraphVertexLabels(D, list);
  return D;
end);

InstallMethod(DigraphCons,
"for IsImmutableDigraph, a number of vertices, source, and range",
[IsImmutableDigraph, IsInt, IsList, IsList],
{_, N, src, ran}
-> MakeImmutable(DigraphCons(IsMutableDigraph, N, src, ran)));

InstallMethod(DigraphCons,
"for IsImmutableDigraph, a list of vertices, source, and range",
[IsImmutableDigraph, IsList, IsList, IsList],
{_, domain, src, ran} ->
MakeImmutable(DigraphCons(IsMutableDigraph, domain, src, ran)));

InstallMethod(Digraph, "for a filter and a record", [IsFunction, IsRecord],
DigraphCons);

InstallMethod(Digraph, "for a record", [IsRecord],
record -> DigraphCons(IsImmutableDigraph, record));

InstallMethod(Digraph, "for a filter and a list", [IsFunction, IsList],
DigraphCons);

InstallMethod(Digraph, "for a list", [IsList],
list -> DigraphCons(IsImmutableDigraph, list));

InstallMethod(Digraph, "for a filter, a list, and a function",
[IsFunction, IsList, IsFunction],
DigraphCons);

InstallMethod(Digraph, "for a list and a function", [IsList, IsFunction],
{list, func} -> DigraphCons(IsImmutableDigraph, list, func));

InstallMethod(Digraph, "for a filter, integer, list, and list",
[IsFunction, IsInt, IsList, IsList],
DigraphCons);

InstallMethod(Digraph, "for an integer, list, and list",
[IsInt, IsList, IsList],
{n, src, ran} -> DigraphCons(IsImmutableDigraph, n, src, ran));

InstallMethod(Digraph, "for a filter, list, list, and list",
[IsFunction, IsList, IsList, IsList],
DigraphCons);

InstallMethod(Digraph, "for a list, list, and list", [IsList, IsList, IsList],
{dom, src, ran} -> DigraphCons(IsImmutableDigraph, dom, src, ran));

InstallMethod(Digraph, "for a string naming a digraph", [IsString],
function(name)
  # edge case to avoid interfering with Digraph([])
  if name = "" then
    TryNextMethod();
  fi;

  # standardise string format to search database
  name := LowercaseString(name);
  RemoveCharacters(name, " \n\t\r");

  # load database if not already done
  DIGRAPHS_LoadNamedDigraphs();

  if not name in RecNames(DIGRAPHS_NamedDigraphs) then
    ErrorNoReturn("named digraph <name> not found; see ListNamedDigraphs,");
  fi;
  return DigraphFromDiSparse6String(DIGRAPHS_NamedDigraphs.(name));
end);

InstallMethod(ListNamedDigraphs,
"for a string and a pos int",
[IsString, IsPosInt],
function(s, level)
  local l, cands, func;
  # standardise request
  s := LowercaseString(s);
  RemoveCharacters(s, " \n\t\r");
  l := Length(s);

  # load database if not already done
  DIGRAPHS_LoadNamedDigraphs();

  # retrieve candidates
  cands := RecNames(DIGRAPHS_NamedDigraphs);
  if l = 0 then
    return cands;
  fi;

  # print warning if level higher than ones here that have methods
  if level > 3 then
    Info(InfoWarning, 1, "ListNamedDigraphs: second argument <level> is");
    Info(InfoWarning, 1, "greater than level of greatest flexibility.");
    Info(InfoWarning, 1, "Using <level> = 3 instead.");
    level := 3;
  fi;

  if level = 1 then
    func := c -> Length(c) >= l and c{[1 .. l]} = s;
  elif level = 2 then
    func := c -> PositionSublist(c, s) <> fail;
  else
    s := Filtered(s, x -> IsDigitChar(x) or IsAlphaChar(x));
    func := c -> PositionSublist(Filtered(c, x -> IsDigitChar(x) or
                                                  IsAlphaChar(x)), s) <> fail;
  fi;
  return Filtered(cands, func);
end);

# if search function called with no level, assume a substring search with
# special chars
InstallMethod(ListNamedDigraphs, "for a string", [IsString],
x -> ListNamedDigraphs(x, 2));

########################################################################
# 6. Printing, viewing, strings
########################################################################

InstallMethod(ViewString, "for a digraph", [IsDigraph],
function(D)
  local n, m, suffix, display_nredges, display_digraph, displayed_bipartite,
  str, x;

  n := DigraphNrVertices(D);
  m := DigraphNrEdges(D);
  suffix := "";
  display_nredges := true;
  display_digraph := true;
  displayed_bipartite := false;

  str := "<";
  if IsMutableDigraph(D) then
    Append(str, "mutable ");
  elif IsImmutableDigraph(D) then
    Append(str, "immutable ");
  fi;

  Assert(1, IsMutableDigraph(D) or IsImmutableDigraph(D));

  if m = 0 then
    if IsImmutableDigraph(D) then
      SetIsEmptyDigraph(D, true);
    fi;
    Append(str, "empty ");
    display_nredges := false;
  elif n > 1 then
    if HasIsCycleDigraph(D) and IsCycleDigraph(D) then
      Append(str, "cycle ");
      display_nredges := false;
    elif HasIsChainDigraph(D) and IsChainDigraph(D) then
      Append(str, "chain ");
      display_nredges := false;
    elif HasIsCompleteDigraph(D) and IsCompleteDigraph(D) then
      Append(str, "complete ");
      display_nredges := false;
    elif HasIsCompleteBipartiteDigraph(D) and IsCompleteBipartiteDigraph(D) then
      Append(str, "complete bipartite ");
      displayed_bipartite := true;
    elif HasIsCompleteMultipartiteDigraph(D)
        and IsCompleteMultipartiteDigraph(D) then
      Append(str, "complete multipartite ");
    elif (HasIsJoinSemilatticeDigraph(D) and IsJoinSemilatticeDigraph(D))
        or (HasIsMeetSemilatticeDigraph(D) and IsMeetSemilatticeDigraph(D)) then
      if HasIsPlanarDigraph(D) and IsPlanarDigraph(D) then
        Append(str, "planar ");
      fi;
      if HasIsLatticeDigraph(D) and IsLatticeDigraph(D) then
        Append(str, "lattice ");
      elif HasIsJoinSemilatticeDigraph(D) and IsJoinSemilatticeDigraph(D) then
        Append(str, "join semilattice ");
      elif HasIsMeetSemilatticeDigraph(D) and IsMeetSemilatticeDigraph(D) then
        Append(str, "meet semilattice ");
      fi;
    elif (HasIsUndirectedTree(D) and IsUndirectedTree(D))
        or (HasIsDirectedTree(D) and IsDirectedTree(D)) then
      if HasIsUndirectedTree(D) and IsUndirectedTree(D) then
        Append(str, "un");
      fi;
      Append(str, "directed tree ");
      display_nredges := false;
      display_digraph := false;
    elif (HasIsUndirectedForest(D) and IsUndirectedForest(D))
        or (HasIsDirectedForest(D) and IsDirectedForest(D)) then
      if HasIsUndirectedForest(D) and IsUndirectedForest(D) then
        Append(str, "un");
      fi;
      Append(str, "directed forest ");
      display_nredges := false;
      display_digraph := false;
      if HasDigraphNrConnectedComponents(D) then
        suffix := Concatenation(String(DigraphNrConnectedComponents(D)),
                                " components");
      fi;
    elif HasIsTournament(D) and IsTournament(D) then
      Append(str, "tournament ");
      display_nredges := false;
      display_digraph := false;
    else
      if HasIsPlanarDigraph(D) and IsPlanarDigraph(D) then
        Append(str, "planar ");
      fi;
      if HasIsEulerianDigraph(D) and IsEulerianDigraph(D) then
        Append(str, "Eulerian ");
        if HasIsHamiltonianDigraph(D) and IsHamiltonianDigraph(D) then
          Append(str, "and ");
        fi;
      fi;
      if HasIsHamiltonianDigraph(D) and IsHamiltonianDigraph(D) then
        Append(str, "Hamiltonian ");
      fi;
      if HasIsStronglyConnectedDigraph(D) and IsStronglyConnectedDigraph(D)
          and not (HasIsEulerianDigraph(D) and IsEulerianDigraph(D))
          and not (HasIsHamiltonianDigraph(D) and IsHamiltonianDigraph(D))
          and not (HasIsSymmetricDigraph(D) and IsSymmetricDigraph(D)) then
        Append(str, "strongly connected ");
      fi;
      if HasIsBiconnectedDigraph(D) and IsBiconnectedDigraph(D) then
        Append(str, "biconnected ");
      elif ((HasIsSymmetricDigraph(D) and IsSymmetricDigraph(D))
            or not (HasIsStronglyConnectedDigraph(D)
                    and IsStronglyConnectedDigraph(D)))
          and not (HasIsTournament(D) and IsTournament(D))
          and not (HasIsHamiltonianDigraph(D) and IsHamiltonianDigraph(D))
          and not (HasIsEulerianDigraph(D) and IsEulerianDigraph(D))
          and HasIsConnectedDigraph(D) and IsConnectedDigraph(D) then
        Append(str, "connected ");
      fi;
      if HasIsBipartiteDigraph(D) and IsBipartiteDigraph(D) then
        Append(str, "bipartite ");
        displayed_bipartite := true;
      fi;
      if HasIsEdgeTransitive(D) and IsEdgeTransitive(D) and
          HasIsVertexTransitive(D) and IsVertexTransitive(D) then
        Append(str, "edge- and vertex-transitive ");
      elif HasIsEdgeTransitive(D) and IsEdgeTransitive(D) then
        Append(str, "edge-transitive ");
      elif HasIsVertexTransitive(D) and IsVertexTransitive(D) then
        Append(str, "vertex-transitive ");
      elif HasIsRegularDigraph(D) and IsRegularDigraph(D) then
        Append(str, "regular ");
      elif HasIsOutRegularDigraph(D) and IsOutRegularDigraph(D) then
        Append(str, "out-regular ");
      elif HasIsInRegularDigraph(D) and IsInRegularDigraph(D) then
        Append(str, "in-regular ");
      fi;
      if HasIsAcyclicDigraph(D) and IsAcyclicDigraph(D) then
        Append(str, "acyclic ");
      elif HasIsPartialOrderDigraph(D) and IsPartialOrderDigraph(D) then
        Append(str, "partial order ");
      elif HasIsEquivalenceDigraph(D) and IsEquivalenceDigraph(D) then
        Append(str, "equivalence ");
      elif HasIsFunctionalDigraph(D) and IsFunctionalDigraph(D) then
        Append(str, "functional ");
        display_nredges := false;
      elif HasIsPreorderDigraph(D) and IsPreorderDigraph(D) then
        Append(str, "preorder ");
      else
        if HasIsReflexiveDigraph(D) and IsReflexiveDigraph(D) then
          Append(str, "reflexive ");
        fi;
        if HasIsSymmetricDigraph(D) and IsSymmetricDigraph(D) then
          Append(str, "symmetric ");
        elif HasIsAntisymmetricDigraph(D) and IsAntisymmetricDigraph(D)
            and not (HasIsTournament(D) and IsTournament(D)) then
          Append(str, "antisymmetric ");
        fi;
        if HasIsTransitiveDigraph(D) and IsTransitiveDigraph(D) then
          Append(str, "transitive ");
        fi;
      fi;
    fi;
  fi;

  if IsMultiDigraph(D) then
    Append(str, "multi");
  fi;

  if display_digraph then
    Append(str, "digraph ");
  fi;
  Append(str, "with ");

  if displayed_bipartite then
    x := List(DigraphBicomponents(D), Length);
    Append(str, "bicomponent");
    if x[1] = x[2] then
      Append(str, "s of size ");
    else
      Append(str, " sizes ");
      Append(str, String(x[1]));
      Append(str, " and ");
    fi;
    Append(str, String(x[2]));
    Append(str, ">");
    return str;
  fi;

  Append(str, String(n));
  if n = 1 then
    Append(str, " vertex");
  else
    Append(str, " vertices");
  fi;
  if display_nredges then
    Append(str, ", ");
    Append(str, String(m));
    if m = 1 then
      Append(str, " edge");
    else
      Append(str, " edges");
    fi;
  fi;
  if not IsEmpty(suffix) then
    Append(str, ", ");
    Append(str, suffix);
  fi;
  Append(str, ">");
  return str;
end);

InstallMethod(PrintString, "for a digraph", [IsDigraph], String);

InstallMethod(String, "for a digraph",
[IsDigraph],
function(D)
  local n, N, i, mut, streps, outnbs_rep, lengths, strings,
        out_neighbours_string, creators_streps, creators_props, props;
  if IsMutableDigraph(D) then
    mut := "IsMutableDigraph, ";
  else
    mut := "";
  fi;
  if IsSymmetricDigraph(D) and not DigraphHasLoops(D) then
    streps := [Graph6String, Sparse6String];
    creators_streps := ["DigraphFromGraph6String",
                        "DigraphFromSparse6String"];
  else
    streps := [Digraph6String, DiSparse6String];
    creators_streps := ["DigraphFromDigraph6String",
                        "DigraphFromDiSparse6String"];
  fi;
  streps  := List(streps, f -> f(D));
  strings := [];
  for n in [1 .. Length(streps)] do
    Add(strings, Concatenation(creators_streps[n], "(", mut, "\"",
                 ReplacedString(streps[n], "\\", "\\\\"), "\"", ")"));
  od;

  out_neighbours_string := String(OutNeighbours(D));
  # print empty lists with two spaces for consistency
  # see https://github.com/gap-system/gap/pull/5418
  out_neighbours_string := ReplacedString(out_neighbours_string, "[ ]", "[  ]");
  outnbs_rep := Concatenation("Digraph(", mut, out_neighbours_string, ")");
  Add(strings, String(outnbs_rep));

  N              := DigraphNrVertices(D);
  props          := [x -> IsCycleDigraph(x)
                          and x = CycleDigraph(DigraphNrVertices(x)),
                     IsCompleteDigraph,
                     x -> IsChainDigraph(x)
                          and x = ChainDigraph(DigraphNrVertices(x)),
                     IsEmptyDigraph];
  creators_props := ["CycleDigraph",
                     "CompleteDigraph",
                     "ChainDigraph",
                     "EmptyDigraph"];
  for i in [1 .. Length(props)] do
    if props[i](D) then
      Add(strings, Concatenation(creators_props[i], "(", mut, String(N), ")"));
    fi;
  od;

  lengths := List(strings, Length);
  return strings[Position(lengths, Minimum(lengths))];
end);

########################################################################
# 7. Operators
########################################################################

InstallMethod(\=, "for two digraphs", [IsDigraph, IsDigraph], DIGRAPH_EQUALS);

InstallMethod(\<, "for two digraphs", [IsDigraph, IsDigraph], DIGRAPH_LT);

########################################################################
# 8. Digraph by-something constructors
########################################################################

InstallMethod(DigraphByAdjacencyMatrixConsNC,
"for IsMutableDigraph and a homogeneous list",
[IsMutableDigraph, IsHomogeneousList],
function(_, mat)
  local add_edge, n, list, i, j;

  if IsInt(mat[1][1]) then
    add_edge := function(i, j)
      local k;
      for k in [1 .. mat[i][j]] do
        Add(list[i], j);
      od;
    end;
  else  # boolean matrix
    add_edge := function(i, j)
      if mat[i][j] then
        Add(list[i], j);
      fi;
    end;
  fi;

  n := Length(mat);
  list := EmptyPlist(n);
  for i in [1 .. n] do
    list[i] := [];
    for j in [1 .. n] do
      add_edge(i, j);
    od;
  od;
  return DigraphNC(IsMutableDigraph, list);
end);

InstallMethod(DigraphByAdjacencyMatrixCons,
"for IsMutableDigraph and a homogeneous list",
[IsMutableDigraph, IsHomogeneousList],
function(_, mat)
  local n, i, j;
  n := Length(mat);
  if not IsRectangularTable(mat) or Length(mat[1]) <> n then
    ErrorNoReturn("the argument <mat> must be a square matrix,");
  elif not IsBool(mat[1][1]) then
    for i in [1 .. n] do
      for j in [1 .. n] do
        if not (IsInt(mat[i][j]) and mat[i][j] >= 0) then
          ErrorNoReturn("the argument <mat> must be a matrix of ",
                        "non-negative integers,");
        fi;
      od;
    od;
  fi;
  return DigraphByAdjacencyMatrixConsNC(IsMutableDigraph, mat);
end);

InstallMethod(DigraphByAdjacencyMatrixCons,
"for IsMutableDigraph and an empty list",
[IsMutableDigraph, IsList and IsEmpty], DigraphByAdjacencyMatrixConsNC);

InstallMethod(DigraphByAdjacencyMatrixConsNC,
"for IsMutableDigraph and an empty list",
[IsMutableDigraph, IsList and IsEmpty],
{_, dummy} -> EmptyDigraph(IsMutableDigraph, 0));

InstallMethod(DigraphByAdjacencyMatrixCons,
"for IsImmutableDigraph and a homogeneous list",
[IsImmutableDigraph, IsHomogeneousList],
function(_, mat)
  local D;
  D := MakeImmutable(DigraphByAdjacencyMatrixCons(IsMutableDigraph, mat));
  if IsEmpty(mat) or IsInt(mat[1][1]) then
    SetAdjacencyMatrix(D, mat);
  else
    Assert(1, IsBool(mat[1][1]));
    SetBooleanAdjacencyMatrix(D, mat);
  fi;
  return D;
end);

InstallMethod(DigraphByAdjacencyMatrix, "for a function and a homogeneous list",
[IsFunction, IsHomogeneousList],
DigraphByAdjacencyMatrixCons);

InstallMethod(DigraphByAdjacencyMatrix, "for a homogeneous list",
[IsHomogeneousList],
mat -> DigraphByAdjacencyMatrixCons(IsImmutableDigraph, mat));

InstallMethod(DigraphByEdgesCons,
"for IsMutableDigraph, a list, and an integer",
[IsMutableDigraph, IsList, IsInt],
function(_, edges, n)
  local pos, list, edge;
  if IsEmpty(edges) then
    return NullDigraph(IsMutableDigraph, n);
  fi;
  pos := 0;
  for edge in edges do
    pos := pos + 1;
    if not IsList(edge) then
      ErrorNoReturn("the 1st argument (list of edges) must be a list of ",
                    "lists, but found ",
                    TNAM_OBJ(edge),
                    " in position ",
                    pos);
    elif Length(edge) <> 2 then
      ErrorNoReturn("the 1st argument (list of edges) must be a list of lists ",
                    "of length 2, found ", edge, " (length ", Length(edge),
                    " in position ", pos, ")");
    elif not IsPosInt(edge[1]) or not IsPosInt(edge[2]) then
      ErrorNoReturn("the 1st argument (list of edges) must be pairs of ",
                    "positive integers but found ", edge, " in position ", pos);
    elif edge[1] > n or edge[2] > n then
      ErrorNoReturn("the 1st argument (list of edges) must be pairs of ",
                    "positive integers <= ", n, " but found ", edge,
                    " in position ", pos);
    fi;
  od;
  list := List([1 .. n], x -> []);
  for edge in edges do
    Add(list[edge[1]], edge[2]);
  od;
  return DigraphNC(IsMutableDigraph, list);
end);

InstallMethod(DigraphByEdgesCons, "for IsMutableDigraph and a list",
[IsMutableDigraph, IsList],
function(_, edges)
  local n;
  if IsEmpty(edges) then
    n := 0;
  else
    n := Maximum(List(edges, Maximum));
  fi;
  return DigraphByEdgesCons(IsMutableDigraph, edges, n);
end);

InstallMethod(DigraphByEdgesCons, "for an ImmutableDigraph and a list",
[IsImmutableDigraph, IsList],
function(_, edges)
  local D;
  D := MakeImmutable(DigraphByEdges(IsMutableDigraph, edges));
  SetDigraphEdges(D, edges);
  SetDigraphNrEdges(D, Length(edges));
  return D;
end);

InstallMethod(DigraphByEdgesCons,
"for IsImmutableDigraph, a list, and an integer",
[IsImmutableDigraph, IsList, IsInt],
function(_, edges, n)
  local D;
  D := MakeImmutable(DigraphByEdges(IsMutableDigraph, edges, n));
  SetDigraphEdges(D, edges);
  SetDigraphNrEdges(D, Length(edges));
  return D;
end);

InstallMethod(DigraphByEdges, "for a list and an integer",
[IsList, IsInt],
{edges, n} -> DigraphByEdgesCons(IsImmutableDigraph, edges, n));

InstallMethod(DigraphByEdges, "for a list", [IsList],
edges -> DigraphByEdgesCons(IsImmutableDigraph, edges));

InstallMethod(DigraphByEdges, "for a function, a list, and an integer",
[IsFunction, IsList, IsInt], DigraphByEdgesCons);

InstallMethod(DigraphByEdges, "for a function and a list",
[IsFunction, IsList], DigraphByEdgesCons);

InstallMethod(DigraphByInNeighboursCons, "for IsMutableDigraph, and a list",
[IsMutableDigraph, IsList],
function(_, list)
  local n, x;
  n := Length(list);  # number of vertices
  for x in list do
    if not ForAll(x, i -> IsPosInt(i) and i <= n) then
      ErrorNoReturn("the argument <list> must be a list of lists of positive ",
                    "integers not exceeding the length of the argument,");
    fi;
  od;
  return DigraphByInNeighboursConsNC(IsMutableDigraph, list);
end);

InstallMethod(DigraphByInNeighboursCons, "for IsImmutableDigraph and a list",
[IsImmutableDigraph, IsList],
function(_, list)
  local D;
  D := MakeImmutable(DigraphByInNeighboursCons(IsMutableDigraph, list));
  SetInNeighbours(D, list);
  return D;
end);

InstallMethod(DigraphByInNeighboursConsNC, "for IsMutableDigraph and a list",
[IsMutableDigraph, IsList],
{_, list} -> DigraphNC(IsMutableDigraph, DIGRAPH_IN_OUT_NBS(list)));

InstallMethod(DigraphByInNeighboursConsNC, "for IsImmutableDigraph and a list",
[IsImmutableDigraph, IsList],
function(_, list)
  local D;
  D := MakeImmutable(DigraphByInNeighboursConsNC(IsMutableDigraph, list));
  SetInNeighbours(D, list);
  return D;
end);

InstallMethod(DigraphByInNeighbours, "for a list", [IsList],
list -> DigraphByInNeighboursCons(IsImmutableDigraph, list));

InstallMethod(DigraphByInNeighbours, "for a function and a list",
[IsFunction, IsList],
DigraphByInNeighboursCons);

########################################################################
# 9. Converters to/from other types -> digraph . . .
########################################################################

InstallMethod(AsDigraphCons, "for IsMutableDigraph and a binary relation",
[IsMutableDigraph, IsBinaryRelation],
function(_, rel)
  local dom, list, i;
  dom := GeneratorsOfDomain(UnderlyingDomainOfBinaryRelation(rel));
  if not IsRange(dom) or dom[1] <> 1 then
    ErrorNoReturn("the argument <rel> must be a binary relation ",
                  "on the domain [1 .. n] for some positive integer n,");
  fi;
  list := EmptyPlist(Length(dom));
  for i in dom do
    list[i] := ImagesElm(rel, i);
  od;
  return Digraph(IsMutableDigraph, list);
end);

InstallMethod(AsDigraphCons, "for IsImmutableDigraph and a binary relation",
[IsImmutableDigraph, IsBinaryRelation],
function(_, rel)
  local D;
  D := MakeImmutable(AsDigraph(IsMutableDigraph, rel));
  SetIsMultiDigraph(D, false);
  if HasIsReflexiveBinaryRelation(rel) then
    SetIsReflexiveDigraph(D, IsReflexiveBinaryRelation(rel));
  fi;
  if HasIsSymmetricBinaryRelation(rel) then
    SetIsSymmetricDigraph(D, IsSymmetricBinaryRelation(rel));
  fi;
  if HasIsTransitiveBinaryRelation(rel) then
    SetIsTransitiveDigraph(D, IsTransitiveBinaryRelation(rel));
  fi;
  if HasIsAntisymmetricBinaryRelation(rel) then
    SetIsAntisymmetricDigraph(D, IsAntisymmetricBinaryRelation(rel));
  fi;
  return D;
end);

InstallMethod(AsDigraph, "for a binary relation", [IsBinaryRelation],
rel -> AsDigraphCons(IsImmutableDigraph, rel));

InstallMethod(AsDigraph, "for a function and a binary relation",
[IsFunction, IsBinaryRelation], AsDigraphCons);

InstallMethod(AsDigraphCons,
"for IsMutableDigraph, a transformation, and an integer",
[IsMutableDigraph, IsTransformation, IsInt],
function(_, f, n)
  local list, x, i;
  if n < 0 then
    ErrorNoReturn("the 2nd argument <n> should be a non-negative integer,");
  fi;

  list := EmptyPlist(n);
  for i in [1 .. n] do
    x := i ^ f;
    if x > n then
      return fail;
    fi;
    list[i] := [x];
  od;
  return DigraphNC(IsMutableDigraph, list);
end);

InstallMethod(AsDigraphCons,
"for IsImmutableDigraph, a transformation, and an integer",
[IsImmutableDigraph, IsTransformation, IsInt],
function(_, f, n)
  local D;
  D := AsDigraph(IsMutableDigraph, f, n);
  if D <> fail then
    D := MakeImmutable(D);
    SetDigraphNrEdges(D, n);
    SetIsMultiDigraph(D, false);
    SetIsFunctionalDigraph(D, true);
  fi;
  return D;
end);

InstallMethod(AsDigraph, "for a function, a transformation, and an integer",
[IsFunction, IsTransformation, IsInt], AsDigraphCons);

InstallMethod(AsDigraph, "for a transformation and an integer",
[IsTransformation, IsInt],
{t, n} -> AsDigraphCons(IsImmutableDigraph, t, n));

InstallMethod(AsDigraph, "for a function and a transformation",
[IsFunction, IsTransformation],
{func, t} -> AsDigraphCons(func, t, DegreeOfTransformation(t)));

InstallMethod(AsDigraph, "for a transformation", [IsTransformation],
t -> AsDigraphCons(IsImmutableDigraph, t, DegreeOfTransformation(t)));

InstallMethod(AsDigraph, "for a function, a perm, and an integer",
[IsFunction, IsPerm, IsInt],
{func, p, n} -> AsDigraphCons(func, AsTransformation(p), n));

InstallMethod(AsDigraph, "for a perm and an integer",
[IsPerm, IsInt],
{p, n} -> AsDigraph(AsTransformation(p), n));

InstallMethod(AsDigraph, "for a function and a perm",
[IsFunction, IsPerm],
{func, p} -> AsDigraph(func, AsTransformation(p)));

InstallMethod(AsDigraph, "for a perm", [IsPerm],
p -> AsDigraph(AsTransformation(p)));

InstallMethod(AsDigraphCons,
"for IsMutableDigraph, a partial perm, and an integer",
[IsMutableDigraph, IsPartialPerm, IsInt],
function(_, f, n)
  local list, x, i;
  if n < 0 then
    ErrorNoReturn("the 2nd argument <n> should be a non-negative integer,");
  fi;

  list := EmptyPlist(n);
  for i in [1 .. n] do
    x := i ^ f;
    if x > n then
      return fail;
    elif x <> 0 then
      list[i] := [x];
    else
      list[i] := [];
    fi;
  od;
  return DigraphNC(IsMutableDigraph, list);
end);

InstallMethod(AsDigraphCons,
"for IsImmutableDigraph, a partial perm, and an integer",
[IsImmutableDigraph, IsPartialPerm, IsInt],
function(_, f, n)
  local D;
  D := AsDigraph(IsMutableDigraph, f, n);
  if D <> fail then
    D := MakeImmutable(D);
    SetIsMultiDigraph(D, false);
  fi;
  return D;
end);

InstallMethod(AsDigraph, "for a function, a partial perm, and an integer",
[IsFunction, IsPartialPerm, IsInt], AsDigraphCons);

InstallMethod(AsDigraph, "for a partial perm and an integer",
[IsPartialPerm, IsInt],
{t, n} -> AsDigraphCons(IsImmutableDigraph, t, n));

InstallMethod(AsDigraph, "for a function and a partial perm",
[IsFunction, IsPartialPerm],
{func, t} -> AsDigraphCons(func, t, Maximum(DegreeOfPartialPerm(t),
                                            CodegreeOfPartialPerm(t))));

InstallMethod(AsDigraph, "for a partial perm", [IsPartialPerm],
t -> AsDigraphCons(IsImmutableDigraph, t, Maximum(DegreeOfPartialPerm(t),
                                                  CodegreeOfPartialPerm(t))));

InstallMethod(AsBinaryRelation, "for a digraph", [IsDigraphByOutNeighboursRep],
function(D)
  local rel;
  if DigraphHasNoVertices(D) then
    ErrorNoReturn("the argument <D> must be a digraph with at least 1 ",
                  "vertex,");
  elif IsMultiDigraph(D) then
    ErrorNoReturn("the argument <D> must be a digraph with no multiple edges");
  fi;
  # Can translate known attributes of <D> to the relation, e.g. symmetry
  rel := BinaryRelationOnPointsNC(OutNeighbours(D));
  if HasIsReflexiveDigraph(D) then
    SetIsReflexiveBinaryRelation(rel, IsReflexiveDigraph(D));
  fi;
  if HasIsSymmetricDigraph(D) then
    SetIsSymmetricBinaryRelation(rel, IsSymmetricDigraph(D));
  fi;
  if HasIsTransitiveDigraph(D) then
    SetIsTransitiveBinaryRelation(rel, IsTransitiveDigraph(D));
  fi;
  if HasIsAntisymmetricDigraph(D) then
    SetIsAntisymmetricBinaryRelation(rel, IsAntisymmetricDigraph(D));
  fi;
  return rel;
end);

InstallMethod(AsSemigroup, "for a function and a digraph",
[IsFunction, IsDigraph],
function(filt, D)
  local red, top, im, max, n, gens, i;

  if filt <> IsPartialPermSemigroup then
    TryNextMethod();
  elif not IsJoinSemilatticeDigraph(D) then
    if IsMeetSemilatticeDigraph(D) then
      return AsSemigroup(IsPartialPermSemigroup,
                         DigraphReverse(DigraphMutableCopyIfMutable(D)));
    fi;
    ErrorNoReturn("the 2nd argument <D> must be digraph that is a join or ",
                  "meet semilattice,");
  fi;

  D   := DigraphMutableCopyIfMutable(D);
  red := DigraphReflexiveTransitiveReduction(D);
  top := DigraphTopologicalSort(D);
  # im[i] will store the image of the idempotent partial perm corresponding to
  # vertex i of the argument <D>
  im         := [];
  im[top[1]] := [];
  max        := 1;

  n := DigraphNrVertices(D);
  # For each vertex, the corresponding idempotent has an image
  # containing all images of idempotents below it.
  for i in [2 .. n] do
    im[top[i]] := Union(List(OutNeighboursOfVertex(red, top[i]), j -> im[j]));
    # When there is only one neighbour, we must add a point to the image to
    # distinguish the two idempotent partial perms.
    if Length(OutNeighboursOfVertex(red, top[i])) = 1 then
      Add(im[top[i]], max);
      max := max + 1;
    fi;
  od;

  # Determine a small generating set
  gens := Filtered([1 .. n], j -> Size(InNeighboursOfVertex(red, j)) < 2);
  return Semigroup(List(gens, g -> PartialPerm(im[g], im[g])));
end);

InstallMethod(AsMonoid, "for a function and a digraph",
[IsFunction, IsDigraph],
function(filt, D)
  if not (filt = IsPartialPermMonoid or filt = IsPartialPermSemigroup) then
    ErrorNoReturn("the 1st argument <filt> must be IsPartialPermMonoid or ",
                  "IsPartialPermSemigroup,");
  elif not IsLatticeDigraph(D) then
    ErrorNoReturn("the 2nd argument <D> must be a lattice digraph,");
  fi;
  return AsSemigroup(IsPartialPermSemigroup, D);
end);

InstallMethod(AsSemigroup,
"for a function, a digraph, and a dense list",
[IsFunction, IsDigraph, IsDenseList, IsDenseList],
function(filt, digraph, gps, homs)
  local red, n, hom_table, reps, rep, top, doms, starts, degs, max, gens, img,
  start, deg, x, queue, j, k, g, y, hom, edge, i, gen;

  if filt <> IsPartialPermSemigroup then
    TryNextMethod();
  elif not IsJoinSemilatticeDigraph(digraph) then
    if IsMeetSemilatticeDigraph(digraph) then
      return AsSemigroup(IsPartialPermSemigroup,
                         DigraphReverse(DigraphMutableCopyIfMutable(digraph)),
                         gps,
                         homs);
    else
      ErrorNoReturn("the second argument must be a join semilattice ",
                    "digraph or a meet semilattice digraph,");
    fi;
  elif not ForAll(gps, IsGroup) then
    ErrorNoReturn("the third argument must be a list of groups,");
  elif not Length(gps) = DigraphNrVertices(digraph) then
    ErrorNoReturn("the third argument must have length equal to the number ",
                  "of vertices in the second argument,");
  fi;

  digraph := DigraphMutableCopyIfMutable(digraph);
  red := DigraphReflexiveTransitiveReduction(digraph);
  MakeImmutable(digraph);
  if not Length(homs) = DigraphNrEdges(red) or
       not ForAll(homs, x -> Length(x) = 3 and
                             IsPosInt(x[1]) and
                             IsPosInt(x[2]) and
                             IsDigraphEdge(red, [x[1], x[2]]) and
                             IsGroupHomomorphism(x[3]) and
                             Source(x[3]) = gps[x[1]] and
                             Range(x[3]) = gps[x[2]]) then
    ErrorNoReturn("the third argument must be a list of triples [i, j, hom] ",
                  "of length equal to the number of edges in the reflexive ",
                  "transitive reduction of the second argument, where [i, j] ",
                  "is an edge in the reflex transitive reduction and hom is ",
                  "a group homomorphism from group i to group j,");
  fi;

  n := DigraphNrVertices(digraph);

  hom_table := List([1 .. n], x -> []);
  for hom in homs do
    hom_table[hom[1]][hom[2]] := hom[3];
  od;

  for edge in DigraphEdges(red) do
    if not IsBound(hom_table[edge[1]][edge[2]]) then
      ErrorNoReturn("the fourth argument must contain a triple [i, j, hom] ",
                    "for each edge [i, j] in the reflexive transitive ",
                    "reduction of the second argument,");
    fi;
  od;

  reps := [];
  for i in [1 .. n] do
    rep := IsomorphismPermGroup(gps[i]);
    rep := rep * SmallerDegreePermutationRepresentation(Image(rep));
    Add(reps, rep);
  od;

  top     := DigraphTopologicalSort(digraph);
  doms    := [];
  starts  := [];
  degs    := List([1 .. n], i -> NrMovedPoints(Image(reps[i])));
  for i in [1 .. n] do
    if degs[i] = 0 then
      degs[i] := 1;
    fi;
  od;
  max             := degs[top[1]] + 1;
  doms[top[1]]    := [1 .. max - 1];
  starts[top[1]]  := 1;

  for i in [2 .. n] do
    doms[top[i]] := Union(List(OutNeighboursOfVertex(red, top[i]),
                               j -> doms[j]));
    Append(doms[top[i]], [max .. max + degs[top[i]] - 1]);
    starts[top[i]] := max;
    max := max + degs[top[i]];
  od;

  gens := [];
  for i in [1 .. n] do
    for gen in GeneratorsOfGroup(gps[top[i]]) do
      img := [];
      start := starts[top[i]];
      deg := degs[top[i]];
      x := ListPerm(gen ^ reps[top[i]]);

      # make sure the partial permutation is defined on the whole domain
      img{[start .. start + deg - 1]} := [1 .. deg] + start - 1;
      # now the actual representation
      img{[start .. start + Length(x) - 1]} := x + start - 1;

      # travel up all the paths from top[i], applying the homomorphisms
      # and storing the results as permutations on the appropriate set
      # of points
      queue := List(OutNeighboursOfVertex(red, top[i]), y -> [top[i], y, gen]);
      while Length(queue) > 0 do
        j     := queue[1][1];
        k     := queue[1][2];
        g     := queue[1][3];
        start := starts[k];
        deg   := degs[k];
        Remove(queue, 1);
        x := g ^ hom_table[j][k];
        Append(queue, List(OutNeighboursOfVertex(red, k), y -> [k, y, x]));
        x := x ^ reps[k];

        # Check that compositions of homomorphisms commute.
        # If img[start] is bound then we have already found some composition of
        # homomorphisms which takes us into gps[k], so we must ensure that this
        # agrees with the composition we are currently considering.
        if IsBound(img[start]) then
          y := PermList(img{[start .. start + deg - 1]} - start + 1);
          if x <> y then
            ErrorNoReturn("the homomorphisms given must form a commutative",
                          " diagram,");
          fi;
        fi;
        x := ListPerm(x);
        img{[start .. start + deg - 1]} := [1 .. deg] + start - 1;
        img{[start .. start + Length(x) - 1]} := x + start - 1;
      od;
      img := Compacted(img);
      Add(gens, PartialPerm(doms[top[i]], img));
    od;
  od;
  return Semigroup(gens);
end);

########################################################################
# 10. Random digraphs
########################################################################

InstallMethod(RandomDigraphCons, "for IsMutableDigraph and an integer",
[IsMutableDigraph, IsInt],
{_, n}
-> RandomDigraphCons(IsMutableDigraph, n, Float(Random(0, n ^ 2) / n ^ 2)));

InstallMethod(RandomDigraphCons, "for IsMutableDigraph and an integer",
[IsImmutableDigraph, IsInt],
{_, n}
-> RandomDigraphCons(IsImmutableDigraph, n, Float(Random(0, n ^ 2) / n ^ 2)));

InstallMethod(RandomDigraphCons, "for IsHamiltonianDigraph and an integer",
[IsHamiltonianDigraph, IsInt],
{_, n}
-> RandomDigraphCons(IsHamiltonianDigraph, n, Float(Random(0, n ^ 2) / n ^ 2)));

InstallMethod(RandomDigraphCons, "for IsEulerianDigraph and an integer",
[IsEulerianDigraph, IsInt],
{_, n}
-> RandomDigraphCons(IsEulerianDigraph, n, Float(Random(0, n ^ 2) / n ^ 2)));

InstallMethod(RandomDigraphCons, "for IsConnectedDigraph and an integer",
[IsConnectedDigraph, IsInt],
{_, n}
-> RandomDigraphCons(IsConnectedDigraph, n, Float(Random(0, n ^ 2) / n ^ 2)));

InstallMethod(RandomDigraphCons, "for IsAcyclicDigraph and an integer",
[IsAcyclicDigraph, IsInt],
{_, n}
-> RandomDigraphCons(IsAcyclicDigraph, n, Float(Random(0, n ^ 2) / n ^ 2)));

InstallMethod(RandomDigraphCons, "for IsSymmetricDigraph and an integer",
[IsSymmetricDigraph, IsInt],
{_, n}
-> RandomDigraphCons(IsSymmetricDigraph, n, Float(Random(0, n ^ 2) / n ^ 2)));

InstallMethod(RandomDigraphCons,
"for IsMutableDigraph, an integer, and a rational",
[IsMutableDigraph, IsInt, IsRat],
{_, n, p} -> RandomDigraphCons(IsMutableDigraph, n, Float(p)));

InstallMethod(RandomDigraphCons,
"for IsImmutableDigraph, an integer, and a rational",
[IsImmutableDigraph, IsInt, IsRat],
{_, n, p} -> RandomDigraphCons(IsImmutableDigraph, n, Float(p)));

InstallMethod(RandomDigraphCons,
"for IsHamiltonianDigraph, an integer, and a rational",
[IsHamiltonianDigraph, IsInt, IsRat],
{_, n, p} -> RandomDigraphCons(IsHamiltonianDigraph, n, Float(p)));

InstallMethod(RandomDigraphCons,
"for IsEulerianDigraph, an integer, and a rational",
[IsEulerianDigraph, IsInt, IsRat],
{_, n, p} -> RandomDigraphCons(IsEulerianDigraph, n, Float(p)));

InstallMethod(RandomDigraphCons,
"for IsConnectedDigraph, an integer, and a rational",
[IsConnectedDigraph, IsInt, IsRat],
{_, n, p} -> RandomDigraphCons(IsConnectedDigraph, n, Float(p)));

InstallMethod(RandomDigraphCons,
"for IsStronglyConnectedDigraph, an integer, and a rational",
[IsStronglyConnectedDigraph, IsInt, IsRat],
{filt, n, p} -> RandomDigraphCons(IsStronglyConnectedDigraph, n, Float(p)));

InstallMethod(RandomDigraphCons,
"for IsAcyclicDigraph, an integer, and a rational",
[IsAcyclicDigraph, IsInt, IsRat],
{_, n, p} -> RandomDigraphCons(IsAcyclicDigraph, n, Float(p)));

InstallMethod(RandomDigraphCons,
"for IsSymmetricDigraph, an integer, and a rational",
[IsSymmetricDigraph, IsInt, IsRat],
{_, n, p} -> RandomDigraphCons(IsSymmetricDigraph, n, Float(p)));

InstallMethod(RandomDigraphCons,
"for IsMutableDigraph, a positive integer, and a float",
[IsMutableDigraph, IsPosInt, IsFloat],
function(_, n, p)
  if p < 0.0 or 1.0 < p then
    ErrorNoReturn("the 2nd argument <p> must be between 0 and 1,");
  fi;
  return DigraphNC(IsMutableDigraph, RANDOM_DIGRAPH(n, p));
end);

# This function takes an existing adjacency list after solely creating
# a Hamiltonian cycle or tree, and randomly adds edges between all
# remaining vertices in the graph.
BindGlobal("DIGRAPHS_FillOutGraph", function(n, p, adjacencyList)
    local vertices, probability, i, j;

    vertices := [1 .. n];
    probability := [0 .. 99];

    for i in vertices do
        for j in vertices do
            if (not (j in adjacencyList[i])) then
                 if Float(Random(probability) / 100) < p then
                    Add(adjacencyList[i], j);
                 fi;
            fi;
        od;
    od;

    return adjacencyList;
end);

InstallMethod(RandomDigraphCons,
"for IsHamiltonianDigraph, a positive integer, and a float",
[IsHamiltonianDigraph, IsPosInt, IsFloat],
function(_, n, p)
    local adjacencyList, vertices, i, startVertex, hamiltonianCycle, x, j;

    adjacencyList := EmptyPlist(n);

    vertices := [1 .. n];

    for i in vertices do
        Add(adjacencyList, []);
    od;

    # Edge Case
    if n = 1 then
        if Float(Random([0 .. 99]) / 100) < p then
            Add(adjacencyList[1], 1);
        fi;
        return DigraphNC(adjacencyList);
    fi;

    # Starting from a random vertex, we create a Hamiltonian cycle
    startVertex := Remove(vertices, Random(vertices));
    hamiltonianCycle := EmptyPlist(n);
    hamiltonianCycle[1] := startVertex;

    # While there are remaining n-1 vertices to be added to the Hamiltonian
    # cycle
    for x in [1 .. n - 1] do
        # Create a random edge from the last vertex in the cycle
        # to a random vertex not in the cycle
        i := hamiltonianCycle[x];
        j := Remove(vertices, Random([1 .. Length(vertices)]));
        Add(hamiltonianCycle, j);
        Add(adjacencyList[i], j);
    od;

    Add(adjacencyList[hamiltonianCycle[n]], startVertex);

    # Once we have created a Hamiltonian cycle, fill out the rest of the graph
    # with random edges according to p
    adjacencyList := DIGRAPHS_FillOutGraph(n, p, adjacencyList);

    return DigraphNC(adjacencyList);
end);

InstallMethod(RandomDigraphCons,
"for IsEulerianDigraph, a positive integer, and a float",
[IsEulerianDigraph, IsPosInt, IsFloat],
function(_, n, p)
    local adjacencyList, vertices, probability, startVertex, circuitVertex, i,
    continueCircuit, verticesToConsider, connectedVertices, verticesToCheck;

    adjacencyList := EmptyPlist(n);

    vertices := [1 .. n];
    probability := [0 .. 99];

    for i in vertices do
        Add(adjacencyList, []);
    od;

    # Edge Case
    if n = 1 then
        if Float(Random(probability) / 100) < p then
            Add(adjacencyList[1], 1);
        fi;
        return DigraphNC(adjacencyList);
    fi;

    # Starting from a random vertex, we create an Eulerian circuit
    startVertex := Random(vertices);
    circuitVertex := startVertex;
    continueCircuit := true;

    # This will keep track of the vertices left to consider when deciding
    # whether to extend the cycle. For example, if we want to extend from the
    # current circuitVertex, we can check - verticesToConsider[circuitVertex] -
    # to see which vertices it hasn't previously considered.
    verticesToConsider := EmptyPlist(n);
    for i in vertices do
        Add(verticesToConsider, [1 .. n]);
    od;

    # Eulerian graphs must be connected, so we store a list of connected
    # vertices and check if a vertex isn't in the list
    connectedVertices := [startVertex];

    while continueCircuit do
        while Length(verticesToConsider[circuitVertex]) > 0 do
            # From the remaining vertices to check, select a random one to see
            # if an edge will be added
            i := Random(verticesToConsider[circuitVertex]);
            Remove(verticesToConsider[circuitVertex],
            Position(verticesToConsider[circuitVertex], i));

            # Check that the edge doesn't already exist
            if (not (i in adjacencyList[circuitVertex])) then
                # First we guarantee that we get a connected graph by checking
                # if the vertex isn't already connected
                if (not (i in connectedVertices)) then
                    Add(adjacencyList[circuitVertex], i);
                    Add(connectedVertices, i);
                    circuitVertex := i;
                    break;
                elif Float(Random(probability) / 100) < p then
                    Add(adjacencyList[circuitVertex], i);
                    circuitVertex := i;
                    break;
                fi;
            fi;
        od;

        # If there are not more vertices to consider, we can end the circuit
        if Length(verticesToConsider[circuitVertex]) = 0 then
            continueCircuit := false;
        fi;
    od;

    # Finish the circuit by adding an edge back to the start vertex
    # (if it isn't already at the start vertex)
    if circuitVertex <> startVertex then
        # If an edge already exists between the finish vertex and the start
        # vertex, we must find another path to the start vertex to avoid
        # generating a multidigraph.
        while (startVertex in adjacencyList[circuitVertex]) do
            # Here we consider all possible edges again, including previously
            # rejected edges, to guarantee a path back to the start
            verticesToCheck := [1 .. n];
            while Length(verticesToCheck) > 0 do
                i := Remove(verticesToCheck,
                            Random([1 .. Length(verticesToCheck)]));
                if (not (i in adjacencyList[circuitVertex])) then
                    Add(adjacencyList[circuitVertex], i);
                    circuitVertex := i;
                    break;
                fi;
            od;
        od;
        Add(adjacencyList[circuitVertex], startVertex);
    fi;

    return DigraphNC(adjacencyList);
end);

InstallMethod(RandomDigraphCons,
"for IsConnectedDigraph, a positive integer, and a float",
[IsConnectedDigraph, IsPosInt, IsFloat],
function(_, n, p)
    local adjacencyList, vertices, startVertex, tree, x, i, j;

    adjacencyList := EmptyPlist(n);

    vertices := [1 .. n];

    for i in vertices do
        Add(adjacencyList, []);
    od;

    # Starting from a random vertex, we first create a tree to guarantee
    # connectivity
    startVertex := Remove(vertices, Random(vertices));
    tree := [startVertex];

    # While there are n-1 remaining vertices to be added to the tree
    for x in [1 .. n - 1] do
        # Create an edge from a random vertex in the tree, to a random vertex
        # outside of it
        i := Random(tree);
        j := Remove(vertices, Random([1 .. Length(vertices)]));
        Add(tree, j);
        Add(adjacencyList[i], j);
    od;

    # Once the tree has been created, we fill out the rest of the graph with
    # random edges according to p
    adjacencyList := DIGRAPHS_FillOutGraph(n, p, adjacencyList);
    return DigraphNC(adjacencyList);
end);

InstallMethod(RandomDigraphCons,
"for IsStronglyConnectedDigraph, a positive integer, and a float",
[IsStronglyConnectedDigraph, IsPosInt, IsFloat],
function(_, n, p)
  local d, adjMatrix, stronglyConnectedComponents,
  scc_a, scc_b, i, random_u, random_v;

  d := RandomDigraph(n, p);

  stronglyConnectedComponents := DigraphStronglyConnectedComponents(d);

  adjMatrix := AdjacencyMatrixMutableCopy(d);

  for i in [1 .. Size(stronglyConnectedComponents.comps) - 1] do
      scc_a := stronglyConnectedComponents.comps[i];
      scc_b := stronglyConnectedComponents.comps[i + 1];

      # add a connection from u to v
      random_u := Random(scc_a);
      random_v := Random(scc_b);

      adjMatrix[random_u][random_v] := 1;
  od;

  # connect end scc to first scc
  scc_a := stronglyConnectedComponents.comps[
    Size(stronglyConnectedComponents.comps)];
  scc_b := stronglyConnectedComponents.comps[1];

  # add a connection from u to v
  random_u := Random(scc_a);
  random_v := Random(scc_b);

  adjMatrix[random_u][random_v] := 1;

  return DigraphByAdjacencyMatrix(adjMatrix);
end);

InstallMethod(RandomDigraphCons,
"for IsAcyclicDigraph, a positive integer, and a float",
[IsAcyclicDigraph, IsPosInt, IsFloat],
function(_, n, p)
    local adjacencyList, vertices, probability, i, j;

    adjacencyList := EmptyPlist(n);

    vertices := [1 .. n];
    probability := [0 .. 99];

    for i in vertices do
        Add(adjacencyList, []);
    od;

    # We shuffle the vertices and treat the order of the vertices as a
    # hierarchy where all vertices to the right of a vertex in the list are
    # potential edges. This idea is that the edges flow downwards in the
    # hierarchy, avoiding cycles.
    Shuffle(vertices);

    # From position i in the list, we consider all vertices j to the right of i
    for i in vertices do
        for j in [i + 1 .. n] do
            if Float(Random(probability) / 100) < p then
                Add(adjacencyList[vertices[i]], vertices[j]);
            fi;
        od;
    od;

    return DigraphNC(adjacencyList);
end);

InstallMethod(RandomDigraphCons,
"for IsSymmetricDigraph, a positive integer, and a float",
[IsSymmetricDigraph, IsPosInt, IsFloat],
function(_, n, p)
    local adjacencyList, vertices, probability, i, j;

    adjacencyList := EmptyPlist(n);

    vertices := [1 .. n];
    probability := [0 .. 99];

    for i in vertices do
        Add(adjacencyList, []);
    od;

    for i in vertices do
        for j in [i .. n] do
            if Float(Random(probability) / 100) < p then
                # If it's a self-loop, only add one edge otherwise we would
                # have a multi-edge.
                if i = j then
                    Add(adjacencyList[i], j);
                else
                    Add(adjacencyList[i], j);
                    Add(adjacencyList[j], i);
                fi;
            fi;
        od;
    od;

    return DigraphNC(adjacencyList);
end);

InstallMethod(RandomDigraphCons,
"for IsImmutableDigraph, a positive integer, and a float",
[IsImmutableDigraph, IsPosInt, IsFloat],
function(_, n, p)
  local D;
  D := MakeImmutable(RandomDigraphCons(IsMutableDigraph, n, p));
  SetIsMultiDigraph(D, false);
  return D;
end);

InstallMethod(RandomDigraph, "for a pos int", [IsPosInt],
n -> RandomDigraphCons(IsImmutableDigraph, n));

InstallMethod(RandomDigraph, "for a pos int and a rational", [IsPosInt, IsRat],
{n, p} -> RandomDigraphCons(IsImmutableDigraph, n, p));

InstallMethod(RandomDigraph, "for a pos int and a float", [IsPosInt, IsFloat],
{n, p} -> RandomDigraphCons(IsImmutableDigraph, n, p));

InstallMethod(RandomDigraph, "for a func and a pos int", [IsFunction, IsPosInt],
RandomDigraphCons);

InstallMethod(RandomDigraph, "for a func, a pos int, and a rational",
[IsFunction, IsPosInt, IsRat], RandomDigraphCons);

InstallMethod(RandomDigraph, "for a func, a pos int, and a float",
[IsFunction, IsPosInt, IsFloat], RandomDigraphCons);

InstallMethod(RandomMultiDigraph, "for a pos int", [IsPosInt],
n -> RandomMultiDigraph(n, Random([1 .. (n * (n - 1)) / 2])));

InstallMethod(RandomMultiDigraph, "for two pos ints", [IsPosInt, IsPosInt],
{n, m} -> DigraphNC(RANDOM_MULTI_DIGRAPH(n, m)));

InstallMethod(RandomTournamentCons, "for IsMutableDigraph and an integer",
[IsMutableDigraph, IsInt],
function(_, n)
  local choice, nodes, list, v, w;
  if n < 0 then
    ErrorNoReturn("the argument <n> must be a non-negative integer,");
  elif n = 0 then
    return EmptyDigraph(IsMutableDigraph, 0);
  fi;
  choice := [true, false];
  nodes  := [1 .. n];
  list   := List(nodes, x -> []);
  for v in nodes do
    for w in [(v + 1) .. n] do
      if Random(choice) then
        Add(list[v], w);
      else
        Add(list[w], v);
      fi;
    od;
  od;
  return DigraphNC(IsMutableDigraph, list);
end);

InstallMethod(RandomTournamentCons, "for IsImmutableDigraph and an integer",
[IsImmutableDigraph, IsInt],
function(_, n)
  local D;
  D := MakeImmutable(RandomTournamentCons(IsMutableDigraph, n));
  SetIsTournament(D, true);
  SetIsMultiDigraph(D, false);
  SetIsSymmetricDigraph(D, n <= 1);
  SetIsEmptyDigraph(D, n <= 1);
  SetDigraphHasLoops(D, false);
  SetDigraphNrEdges(D, Binomial(n, 2));
  return D;
end);

InstallMethod(RandomTournament, "for an integer", [IsInt],
n -> RandomTournamentCons(IsImmutableDigraph, n));

InstallMethod(RandomTournament, "for a func and an integer",
[IsFunction, IsInt], {func, n} -> RandomTournamentCons(func, n));

InstallMethod(RandomLatticeCons, "for IsMutableDigraph and a pos int",
[IsMutableDigraph, IsPosInt],
function(_, n)
  local fam, rand_blist;

  fam := [BlistList([1 .. n], [])];

  while Length(fam) < n do
    rand_blist := List([1 .. n], x -> Random([true, false]));
    UniteSet(fam, List(fam, x -> UnionBlist(x, rand_blist)));
  od;

  return Digraph(IsMutableDigraph, fam, IsSubsetBlist);
end);

InstallMethod(RandomLatticeCons, "for IsImmutableDigraph and a pos int",
[IsImmutableDigraph, IsPosInt],
function(_, n)
  local D;
  D := MakeImmutable(RandomLatticeCons(IsMutableDigraph, n));
  SetIsLatticeDigraph(D, true);
  return D;
end);

InstallMethod(RandomLattice, "for a pos int", [IsPosInt],
n -> RandomLatticeCons(IsImmutableDigraph, n));

InstallMethod(RandomLattice, "for a func and a pos int", [IsFunction, IsPosInt],
RandomLatticeCons);

[ zur Elbe Produktseite wechseln0.90Quellennavigators  Analyse erneut starten  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge