Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/GAP/pkg/digraphs/gap/   (Algebra von RWTH Aachen Version 4.15.1©)  Datei vom 27.8.2025 mit Größe 24 kB image not shown  

Quelle  weights.gi   Sprache: unbekannt

 
#############################################################################
##
##  weights.gi
##  Copyright (C) 2023                                Raiyan Chowdhury
##
##  Licensing information can be found in the README file of this package.
##
#############################################################################
##

#############################################################################
# 1. Edge Weights
#############################################################################

InstallGlobalFunction(EdgeWeightedDigraph,
function(digraph, weights)
  local digraphVertices, nrVertices, u, outNeighbours,
        outNeighbourWeights, idx;
  if IsDigraph(digraph) then
    digraph := DigraphCopy(digraph);
  else
    digraph := Digraph(digraph);
  fi;

  # check all elements of weights is a list
  if not ForAll(weights, IsListOrCollection) then
    ErrorNoReturn("the 2nd argument (list) must be a list of lists,");
  fi;

  digraphVertices := DigraphVertices(digraph);
  nrVertices := Size(digraphVertices);

  # check number there is an edge weight list for vertex u
  if nrVertices <> Size(weights) then
    ErrorNoReturn("the number of out neighbours and weights must be equal,");
  fi;

  # check all elements of weights is a list and size/shape is correct
  for u in digraphVertices do
    outNeighbours := OutNeighbors(digraph)[u];
    outNeighbourWeights := weights[u];

    # check number of out neighbours for u
    # and number of weights given is the same
    if Size(outNeighbours) <> Size(outNeighbourWeights) then
      ErrorNoReturn("the sizes of the out neighbours and weights for vertex ",
                    u, " must be equal,");
    fi;

    # check all elements of out neighbours are appropriate
    for idx in [1 .. Size(outNeighbours)] do
      if not (IsInt(outNeighbourWeights[idx])
              or IsFloat(outNeighbourWeights[idx])
              or IsRat(outNeighbourWeights[idx])) then
        ErrorNoReturn("out neighbour weight must be ",
                      "an integer, float or rational,");
      fi;
    od;
  od;

  SetEdgeWeights(digraph, weights);
  return digraph;
end);

InstallMethod(IsNegativeEdgeWeightedDigraph, "for a digraph with edge weights",
[IsDigraph and HasEdgeWeights],
function(digraph)
  local weights, u, w;
  weights := EdgeWeights(digraph);

  for u in weights do
    for w in u do
      if Float(w) < Float(0) then
        return true;
      fi;
    od;
  od;
  return false;
end);

InstallMethod(EdgeWeightedDigraphTotalWeight,
"for a digraph with edge weights",
[IsDigraph and HasEdgeWeights],
D -> Sum(EdgeWeights(D), Sum));

#############################################################################
# 2. Copies of edge weights
#############################################################################

InstallMethod(EdgeWeightsMutableCopy, "for a digraph with edge weights",
[IsDigraph and HasEdgeWeights],
D -> List(EdgeWeights(D), ShallowCopy));

#############################################################################
# 3. Minimum Spanning Trees
#############################################################################

InstallMethod(EdgeWeightedDigraphMinimumSpanningTree,
"for a digraph with edge weights",
[IsDigraph and HasEdgeWeights],
function(digraph)
  local weights, numberOfVertices, edgeList, u, outNeighbours, idx, v, w, mst,
        mstWeights, partition, i, nrEdges, total, node, x, y, out;

  # check graph is connected
  if not IsConnectedDigraph(digraph) then
    ErrorNoReturn("the argument <digraph> must be a connected digraph,");
  fi;

  weights := EdgeWeights(digraph);

  # create a list of edges containing u-v
  # w: the weight of the edge
  # u: the start vertex
  # v: the finishing vertex of that edge
  numberOfVertices := DigraphNrVertices(digraph);
  edgeList := [];
  for u in DigraphVertices(digraph) do
    outNeighbours := OutNeighboursOfVertex(digraph, u);
    for idx in [1 .. Size(outNeighbours)] do
      v := outNeighbours[idx];  # the out neighbour
      w := weights[u][idx];     # the weight to the out neighbour
      Add(edgeList, [w, u, v]);
    od;
  od;

  # sort edge weights by their weight
  StableSortBy(edgeList, x -> x[1]);

  mst        := EmptyPlist(numberOfVertices);
  mstWeights := EmptyPlist(numberOfVertices);

  partition := PartitionDS(IsPartitionDS, numberOfVertices);

  for v in [1 .. numberOfVertices] do
    Add(mst, []);
    Add(mstWeights, []);
  od;

  i := 1;
  nrEdges := 0;
  total := 0;
  while nrEdges < numberOfVertices - 1 do
    node := edgeList[i];

    w := node[1];
    u := node[2];
    v := node[3];

    i := i + 1;

    x := Representative(partition, u);
    y := Representative(partition, v);

    # if cycle doesn't exist
    if x <> y then
      Add(mst[u], v);
      Add(mstWeights[u], w);
      nrEdges := nrEdges + 1;
      total := total + w;
      Unite(partition, x, y);
    fi;
  od;

  out := EdgeWeightedDigraph(mst, mstWeights);
  SetEdgeWeightedDigraphTotalWeight(out, total);
  return out;
end);

#############################################################################
# 4. Shortest Path
#############################################################################
#
# Three different "shortest path" problems are solved:
# - All pairs:       EdgeWeightedDigraphShortestPaths(digraph)
# - Single source:   EdgeWeightedDigraphShortestPaths(digraph, source)
# - Source and dest: EdgeWeightedDigraphShortestPath (digraph, source, dest)
#
# The "all pairs" problem has two algorithms:
# - Johnson: better for sparse digraphs
# - Floyd-Warshall: better for dense graphs
#
# The "single source" problem has three algorithms:
# - If "all pairs" is already known, extract information for the given source
# - Dijkstra: faster, but cannot handle negative weights
# - Bellman-Ford: slower, but handles negative weights
#
# The "source and destination" problem calls the "single source" problem and
# extracts information for the given destination.
#
# Some of the algorithms handle negative weights, but none of them handles
# negative *cycles*. Negative cycles are checked, and if there are any the
# inner function returns fail and we call TryNextMethod.
#
# Justification and benchmarks are in the experimental part of Raiyan's MSci
# thesis, https://github.com/RaiyanC/CS5199-Proj-Code
#

InstallMethod(EdgeWeightedDigraphShortestPaths,
"for a digraph with edge weights",
[IsDigraph and HasEdgeWeights],
function(digraph)
  local maxNodes, threshold, digraphVertices, nrVertices, nrEdges, result;

  digraphVertices := DigraphVertices(digraph);
  nrVertices := Size(digraphVertices);
  nrEdges := DigraphNrEdges(digraph);

  maxNodes := nrVertices * (nrVertices - 1);

  # For dense graphs we use Floyd-Warshall; for sparse graphs Johnson. The
  # threshold for "dense", based on experiments, is n(n-1)/8 edges.
  threshold := Int(maxNodes / 8);
  if nrEdges <= threshold then
    result := DIGRAPHS_Edge_Weighted_Johnson(digraph);
  else
    result := DIGRAPHS_Edge_Weighted_FloydWarshall(digraph);
  fi;

  # Currently we have no method that works for digraphs with negative cycles.
  # It may be possible if we implement a further algorithm in future.
  if result = fail then
    Info(InfoDigraphs, 1, "<digraph> contains a negative cycle, so there is ",
         "currently no suitable method for EdgeWeightedDigraphShortestPaths");
    TryNextMethod();
  fi;

  return result;
end);

InstallMethod(EdgeWeightedDigraphShortestPaths,
"for a digraph with edge weights and known shortest paths and a pos int",
[IsDigraph and HasEdgeWeights and HasEdgeWeightedDigraphShortestPaths,
 IsPosInt],
function(digraph, source)
  local all_paths;
  if not source in DigraphVertices(digraph) then
    ErrorNoReturn("the 2nd argument <source> must be a vertex of the ",
                  "1st argument <digraph>,");
  fi;
  # Shortest paths are known for all vertices. Extract the one we want.
  all_paths := EdgeWeightedDigraphShortestPaths(digraph);
  return rec(distances := all_paths.distances[source],
             edges     := all_paths.edges[source],
             parents   := all_paths.parents[source]);
end);

InstallMethod(EdgeWeightedDigraphShortestPaths,
"for a digraph with edge weights and a pos int",
[IsDigraph and HasEdgeWeights, IsPosInt],
function(digraph, source)
  local result;
  if not source in DigraphVertices(digraph) then
    ErrorNoReturn("the 2nd argument <source> must be a vertex of the ",
                  "1st argument <digraph>,");
  fi;

  if IsNegativeEdgeWeightedDigraph(digraph) then
    result := DIGRAPHS_Edge_Weighted_Bellman_Ford(digraph, source);
    if result = fail then
      Info(InfoDigraphs, 1, "<digraph> contains a negative cycle, so there is ",
           "currently no suitable method for EdgeWeightedDigraphShortestPaths");
      TryNextMethod();
    fi;
    return result;
  else
    return DIGRAPHS_Edge_Weighted_Dijkstra(digraph, source);
  fi;
end);

InstallMethod(EdgeWeightedDigraphShortestPath,
"for a digraph with edge weights and two pos ints",
[IsDigraph and HasEdgeWeights, IsPosInt, IsPosInt],
function(digraph, source, dest)
  local paths, v, a, current, edge_index;
  if not source in DigraphVertices(digraph) then
    ErrorNoReturn("the 2nd argument <source> must be a vertex of the ",
                  "1st argument <digraph>,");
  elif not dest in DigraphVertices(digraph) then
    ErrorNoReturn("the 3rd argument <dest> must be a vertex of the ",
                  "1st argument <digraph>,");
  fi;

  # No trivial paths
  if source = dest then
    return fail;
  fi;

  # Get shortest paths information for this source vertex
  paths := EdgeWeightedDigraphShortestPaths(digraph, source);

  # Convert to DigraphPath's [v, a] format by exploring backwards from dest
  v := [dest];
  a := [];
  current := dest;
  while current <> source do
    edge_index := paths.edges[current];
    current := paths.parents[current];
    if edge_index = fail or current = fail then
      return fail;
    fi;
    Add(a, edge_index);
    Add(v, current);
  od;

  return [Reversed(v), Reversed(a)];
end);

InstallGlobalFunction(DIGRAPHS_Edge_Weighted_Johnson,
function(digraph)
  local vertices, nrVertices, mutableOuts, mutableWeights, new, v, bellman,
        bellmanDistances, u, outNeighbours, idx, w, distances, parents, edges,
        dijkstra;
  vertices := DigraphVertices(digraph);
  nrVertices := Size(vertices);
  mutableOuts := OutNeighborsMutableCopy(digraph);
  mutableWeights := EdgeWeightsMutableCopy(digraph);

  # add new u that connects to all other v with weight 0
  new := nrVertices + 1;
  mutableOuts[new] := [];
  mutableWeights[new] := [];

  # fill new u
  for v in [1 .. nrVertices] do
    mutableOuts[new][v] := v;
    mutableWeights[new][v] := 0;
  od;

  # calculate shortest paths from the new vertex (could be negative)
  digraph := EdgeWeightedDigraph(mutableOuts, mutableWeights);
  bellman := DIGRAPHS_Edge_Weighted_Bellman_Ford(digraph, new);
  if bellman = fail then
    # negative cycle detected: this algorithm fails
    return fail;
  fi;
  bellmanDistances := bellman.distances;

  # new copy of neighbours and weights
  mutableOuts := OutNeighborsMutableCopy(digraph);
  mutableWeights := EdgeWeightsMutableCopy(digraph);

  # set weight(u, v) equal to weight(u, v) + bell_dist(u) - bell_dist(v)
  # for each edge (u, v)
  for u in vertices do
    outNeighbours := mutableOuts[u];
    for idx in [1 .. Size(outNeighbours)] do
      v                      := outNeighbours[idx];
      w                      := mutableWeights[u][idx];
      mutableWeights[u][idx] := w +
                                bellmanDistances[u] - bellmanDistances[v];
    od;
  od;

  Remove(mutableOuts, new);
  Remove(mutableWeights, new);

  digraph   := EdgeWeightedDigraph(mutableOuts, mutableWeights);
  distances := EmptyPlist(nrVertices);
  parents   := EmptyPlist(nrVertices);
  edges     := EmptyPlist(nrVertices);

  # run dijkstra
  for u in vertices do
    dijkstra     := DIGRAPHS_Edge_Weighted_Dijkstra(digraph, u);
    distances[u] := dijkstra.distances;
    parents[u]   := dijkstra.parents;
    edges[u]     := dijkstra.edges;
  od;

  # correct distances
  for u in vertices do
    for v in vertices do
      if distances[u][v] = fail then
        continue;
      fi;
      distances[u][v] := distances[u][v] +
                         bellmanDistances[v] - bellmanDistances[u];
    od;
  od;

  return rec(distances := distances, parents := parents, edges := edges);
end);

InstallGlobalFunction(DIGRAPHS_Edge_Weighted_FloydWarshall,
function(digraph)
  # Note: we aren't using the C function FLOYD_WARSHALL here since it is set up
  # for a different task: it always creates a distance matrix consisting of two
  # values (edge and non-edge) rather than multiple values; and it only stores
  # distances, and doesn't remember info about the actual paths.  In the future
  # we might want to refactor the C function so we can use it here.
  local weights, adjMatrix, vertices, nrVertices, u, v, edges, outs, idx,
        outNeighbours, w, i, k, distances, parents;
  weights    := EdgeWeights(digraph);
  vertices   := DigraphVertices(digraph);
  nrVertices := Size(vertices);
  outs       := OutNeighbours(digraph);

  # Create adjacency matrix with (minimum weight, edge index), or a hole
  adjMatrix := EmptyPlist(nrVertices);
  for u in vertices do
    adjMatrix[u] := EmptyPlist(nrVertices);
    outNeighbours := outs[u];
    for idx in [1 .. Size(outNeighbours)] do
      v := outNeighbours[idx];  # the out neighbour
      w := weights[u][idx];     # the weight to the out neighbour
      # Use minimum weight edge
      if (not IsBound(adjMatrix[u][v])) or (w < adjMatrix[u][v][1]) then
        adjMatrix[u][v] := [w, idx];
      fi;
    od;
  od;

  # Store shortest paths for single edges
  distances := EmptyPlist(nrVertices);
  parents   := EmptyPlist(nrVertices);
  edges     := EmptyPlist(nrVertices);
  for u in vertices do
    distances[u] := EmptyPlist(nrVertices);
    parents[u]   := EmptyPlist(nrVertices);
    edges[u]     := EmptyPlist(nrVertices);

    for v in vertices do
      distances[u][v] := infinity;
      parents[u][v] := fail;
      edges[u][v] := fail;

      if u = v then
        distances[u][v] := 0;
        # if the same node, then the node has no parents
        parents[u][v] := fail;
        edges[u][v]   := fail;
      elif IsBound(adjMatrix[u][v]) then
        w   := adjMatrix[u][v][1];
        idx := adjMatrix[u][v][2];

        distances[u][v] := w;
        parents[u][v]   := u;
        edges[u][v]     := idx;
      fi;
    od;
  od;

  # try every triple: distance from u to v via k
  for k in vertices do
    for u in vertices do
      if distances[u][k] < infinity then
        for v in vertices do
          if distances[k][v] < infinity then
            if distances[u][k] + distances[k][v] < distances[u][v] then
              distances[u][v] := distances[u][k] + distances[k][v];
              parents[u][v]   := parents[u][k];
              edges[u][v]     := edges[k][v];
            fi;
          fi;
        od;
      fi;
    od;
  od;

  # detect negative cycles
  for i in vertices do
    if distances[i][i] < 0 then
      # digraph contains a negative cycle: this algorithm fails
      return fail;
    fi;
  od;

  # replace infinity with fails
  for u in vertices do
    for v in vertices do
      if distances[u][v] = infinity then
        distances[u][v] := fail;
      fi;
    od;
  od;

  return rec(distances := distances, parents := parents, edges := edges);
end);

InstallGlobalFunction(DIGRAPHS_Edge_Weighted_Dijkstra,
function(digraph, source)
  local weights, vertices, nrVertices, adj, u, outNeighbours, idx, v, w,
        distances, parents, edges, visited, queue, node, currDist, neighbour,
        edgeInfo, distance, i;

  weights    := EdgeWeights(digraph);
  vertices   := DigraphVertices(digraph);
  nrVertices := Size(vertices);

  # Create an adjacency map for the shortest edges: index and weight
  adj := HashMap();
  for u in vertices do
    adj[u] := HashMap();
    outNeighbours := OutNeighbors(digraph)[u];
    for idx in [1 .. Size(outNeighbours)] do
      v := outNeighbours[idx];  # the out neighbour
      w := weights[u][idx];     # the weight to the out neighbour

      # an edge to v already exists
      if v in adj[u] then
        # check if edge weight is less than current weight,
        # and keep track of edge idx
        if w < adj[u][v][1] then
          adj[u][v] := [w, idx];
        fi;
      else  # edge doesn't exist already, so add it
        adj[u][v] := [w, idx];
      fi;
    od;
  od;

  distances := ListWithIdenticalEntries(nrVertices, infinity);
  parents   := EmptyPlist(nrVertices);
  edges     := EmptyPlist(nrVertices);

  distances[source] := 0;
  parents[source]   := fail;
  edges[source]     := fail;

  visited := BlistList(vertices, []);

  # make binary heap by priority of
  # index 1 of each element (the cost to get to the node)
  queue := BinaryHeap({x, y} -> x[1] > y[1]);
  Push(queue, [0, source]);  # the source vertex with cost 0

  while not IsEmpty(queue) do
    node := Pop(queue);

    currDist := node[1];
    u        := node[2];

    if visited[u] then
      continue;
    fi;

    visited[u] := true;

    for neighbour in KeyValueIterator(adj[u]) do
      v        := neighbour[1];
      edgeInfo := neighbour[2];
      w        := edgeInfo[1];
      idx      := edgeInfo[2];

      distance := currDist + w;

      if Float(distance) < Float(distances[v]) then
        distances[v] := distance;

        parents[v] := u;
        edges[v]   := idx;

        if not visited[v] then
          Push(queue, [distance, v]);
        fi;
      fi;
    od;
  od;

  # show fail if no path is possible
  for i in vertices do
    if distances[i] = infinity then
      distances[i] := fail;
      parents[i]   := fail;
      edges[i]     := fail;
    fi;
  od;

  return rec(distances := distances, parents := parents, edges := edges);
end);

InstallGlobalFunction(DIGRAPHS_Edge_Weighted_Bellman_Ford,
function(digraph, source)
  local edgeList, weights, vertices, nrVertices, distances, u, outNeighbours,
        idx, v, w, edge, parents, edges, i, flag, _;

  weights    := EdgeWeights(digraph);
  vertices   := DigraphVertices(digraph);
  nrVertices := Size(vertices);

  edgeList := [];
  for u in DigraphVertices(digraph) do
    outNeighbours := OutNeighbours(digraph)[u];
    for idx in [1 .. Size(outNeighbours)] do
      v := outNeighbours[idx];  # the out neighbour
      w := weights[u][idx];     # the weight to the out neighbour
      Add(edgeList, [w, u, v, idx]);
    od;
  od;

  distances := ListWithIdenticalEntries(nrVertices, infinity);
  parents   := EmptyPlist(nrVertices);
  edges     := EmptyPlist(nrVertices);

  distances[source] := 0;
  parents[source]   := fail;
  edges[source]     := fail;

  # relax all edges: update weight with smallest edges
  flag := true;
  for _ in vertices do
    for edge in edgeList do
      w := edge[1];
      u := edge[2];
      v := edge[3];
      idx := edge[4];

      if distances[u] <> infinity
          and Float(distances[u]) + Float(w) < Float(distances[v]) then
        distances[v] := distances[u] + w;

        parents[v] := u;
        edges[v]   := idx;
        flag       := false;
      fi;
    od;

    if flag then
      break;
    fi;
  od;

  # check for negative cycles
  for edge in edgeList do
    w := edge[1];
    u := edge[2];
    v := edge[3];

    if distances[u] <> infinity
        and Float(distances[u]) + Float(w) < Float(distances[v]) then
      # negative cycle detected: this algorithm fails
      return fail;
    fi;
  od;

  # fill lists with fail if no path is possible
  for i in vertices do
    if distances[i] = infinity then
      distances[i] := fail;
      parents[i]   := fail;
      edges[i]     := fail;
    fi;
  od;

  return rec(distances := distances, parents := parents, edges := edges);
end);

#############################################################################
# 5. Maximum Flow
#############################################################################

InstallMethod(DigraphMaximumFlow, "for an edge weighted digraph",
[IsDigraph and HasEdgeWeights, IsPosInt, IsPosInt],
function(digraph, start, destination)
  local push, relabel, discharge, weights, vertices, nrVertices, outs, capacity,
        flowMatrix, seen, height, excess, queue, u, outNeighbours, i, v, w,
        flows, delta;

  # Push flow from u to v
  push := function(u, v)
    local delta;

    delta := Minimum(excess[u], capacity[u][v] - flowMatrix[u][v]);

    flowMatrix[u][v] := flowMatrix[u][v] + delta;
    flowMatrix[v][u] := flowMatrix[v][u] - delta;
    excess[u]        := excess[u] - delta;
    excess[v]        := excess[v] + delta;

    if excess[v] = delta then
      PlistDequePushBack(queue, v);
    fi;
  end;

  # Decide height of u, which must be above any vertex it can flow to
  relabel := function(u)
    local d, v;
    d := infinity;
    for v in vertices do
      if capacity[u][v] - flowMatrix[u][v] > 0 then
        d := Minimum(d, height[v]);
      fi;
    od;
    if d < infinity then
      height[u] := d + 1;
    fi;
  end;

  # Push all excess flow from u to other vertices
  discharge := function(u)
    local v;
    while excess[u] > 0 do
      if seen[u] <= nrVertices then
        v := seen[u];
        if capacity[u][v] - flowMatrix[u][v] > 0
            and height[u] > height[v] then
          push(u, v);
        else
          seen[u] := seen[u] + 1;
        fi;
      else
        relabel(u);
        seen[u] := 1;
      fi;
    od;
  end;

  # Extract important data
  weights    := EdgeWeights(digraph);
  vertices   := DigraphVertices(digraph);
  nrVertices := Length(vertices);
  outs       := OutNeighbors(digraph);

  # Check input
  if not start in vertices then
    ErrorNoReturn("<start> must be a vertex of <digraph>,");
  elif not destination in vertices then
    ErrorNoReturn("<destination> must be a vertex of <digraph>,");
  fi;

  # Setup data structures
  capacity   := EmptyPlist(nrVertices);
  flowMatrix := EmptyPlist(nrVertices);
  seen       := EmptyPlist(nrVertices);
  height     := EmptyPlist(nrVertices);
  excess     := EmptyPlist(nrVertices);
  queue      := PlistDeque();
  for u in vertices do
    capacity[u]   := ListWithIdenticalEntries(nrVertices, 0);
    flowMatrix[u] := ListWithIdenticalEntries(nrVertices, 0);
    seen[u]       := 1;  # highest vertex to which we've pushed flow from u
    height[u]     := 0;  # proximity to start (further is lower)
    excess[u]     := 0;  # flow coming into u that isn't yet leaving u
    if u <> start and u <> destination then
      PlistDequePushBack(queue, u);
    fi;
  od;

  # Compute total capacity from each u to v
  for u in vertices do
    outNeighbours := outs[u];
    for i in [1 .. Size(outNeighbours)] do
      v := outNeighbours[i];  # the out neighbour
      w := weights[u][i];     # the weight to the out neighbour
      capacity[u][v] := capacity[u][v] + w;
    od;
  od;

  # start vertex is "top", with unlimited flow coming out
  height[start] := nrVertices;
  excess[start] := infinity;

  # Push flow from start to the other vertices
  for v in vertices do
    if v <> start then
      push(start, v);
    fi;
  od;

  # Discharge from vertices until there are none left to do
  while not IsEmpty(queue) do
    u := PlistDequePopFront(queue);
    if u <> start and u <> destination then
      discharge(u);
    fi;
  od;

  # Convert to output format: a list of lists with same shape as weights
  flows := List(weights, l -> EmptyPlist(Length(l)));
  for u in vertices do
    for i in [1 .. Length(outs[u])] do
      v := outs[u][i];
      delta := Minimum(flowMatrix[u][v], weights[u][i]);
      delta := Maximum(0, delta);
      flowMatrix[u][v] := flowMatrix[u][v] - delta;
      flows[u][i] := delta;
    od;
  od;

  return flows;
end);

#############################################################################
# 6. Random edge weighted digraphs
#############################################################################

BindGlobal("DIGRAPHS_RandomEdgeWeightedDigraphFilt",
function(arg...)
  local digraph, outs, weightsSource, weights;
  # Create random digraph
  digraph := CallFuncList(RandomDigraphCons, arg);
  outs := OutNeighbours(digraph);

  # Unique weights are taken randomly from [1..nredges]
  weightsSource := Shuffle([1 .. DigraphNrEdges(digraph)]);
  weights := List(DigraphVertices(digraph),
                  u -> List(outs[u], _ -> Remove(weightsSource)));

  return EdgeWeightedDigraph(digraph, weights);
end);

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

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

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

InstallMethod(RandomUniqueEdgeWeightedDigraph,
"for a function and a pos int", [IsFunction, IsPosInt],
DIGRAPHS_RandomEdgeWeightedDigraphFilt);

InstallMethod(RandomUniqueEdgeWeightedDigraph,
"for a function, a pos int, and a float", [IsFunction, IsPosInt, IsFloat],
DIGRAPHS_RandomEdgeWeightedDigraphFilt);

InstallMethod(RandomUniqueEdgeWeightedDigraph,
"for a function, a pos int, and a rational", [IsFunction, IsPosInt, IsRat],
DIGRAPHS_RandomEdgeWeightedDigraphFilt);

[ Dauer der Verarbeitung: 0.38 Sekunden  (vorverarbeitet)  ]