gramoloss
Version:
Graph theory package for edition and computation
673 lines (672 loc) • 26.7 kB
TypeScript
import { Link } from './link';
import { Vertex, VertexIndex } from './vertex';
import { Coord } from './coord';
import { Option } from "./option";
/**
* For the Dominating Set Algo
* @todo should be removed by implementing a method for IDS
*/
export declare enum DominationVariant {
Independent = 0,
OrientedIndependent = 1
}
export declare class Graph {
vertices: Map<VertexIndex, Vertex>;
links: Map<number, Link>;
matrix: Array<Array<number>>;
private n;
private linksCounter;
constructor(n?: number);
nbVertices(): number;
nbEdges(): number;
nbArcs(): number;
getAvailableVertexIndex(): VertexIndex;
/**
* Add a vertex to the graph.
*/
addVertex(index: VertexIndex, x?: number, y?: number): Vertex;
deleteVertex(v: Vertex): void;
addEdge(u: Vertex | VertexIndex, v: Vertex | VertexIndex): Link | Error;
hasEdge(a: Vertex | VertexIndex, b: Vertex | VertexIndex): boolean;
/**
* Delete the edge between u and v if any.
* If the graph is not simple, there may be several edges between u and v.
* Therefore all the edges between u and v are deleted.
*/
deleteEdge(u: Vertex, v: Vertex): void;
addArc(u: Vertex | VertexIndex, v: Vertex | VertexIndex): Link | Error;
hasArc(a: Vertex | VertexIndex, b: Vertex | VertexIndex): boolean;
degree(v: VertexIndex | Vertex): number;
indegree(v: VertexIndex | Vertex): number;
outdegree(v: VertexIndex | Vertex): number;
print(): void;
/**
* @todo TRANSPOSED
* Only print arcs
*/
printDirectedAdjacencyMatrix(): void;
/**
* Returns an undirected Graph given by its edges.
* @param edgesList
*/
static fromEdges(edgesList: Array<[number, number, string?]>, verticesPositions?: Array<[number, number]>): Graph;
/**
* Returns an undirected Graph given by its edges.
* @param arcs
*/
static fromArcs(arcs: Array<[number, number, string?]>, verticesPositions?: Array<[number, number]>): Graph;
/**
* @returns Petersen graph (10 vertices, 15 edges)
* @see https://en.wikipedia.org/wiki/Petersen_graph
*/
static petersen(): Graph;
/**
* Returns a random graph with n vertices.
* @param n number of vertices. Should be >= 0. Return empty graph if n < 1.
* @param p probabilty of appearance of an edge. Should be between 0 and 1.
*/
static randomGNP(n: number, p: number): Graph;
/**
* Returns a graph with vertex set indices [0, sum(sizes)-1]
* Vi = sum( sizes[k], k < i) + [0, sizes[i]-1]
* For every i and j, every vertex of Vi is adjacent to every vertex of Vj
* @param sizes list of the sizes of the parts
* @example
* For sizes = [5,4,3], the graph has 5+4+3 vertices
* The sum of the degrees is 5*(4+3) + 4*(5+3) + 3*(5+4).
*/
static completeMultipartite(sizes: Array<number>): Graph;
/**
* Special case of `completeMultipartite`
* @param n
* @param m
* @returns
*/
static completeBipartite(n: number, m: number): Graph;
/**
* Returns a random oriented graph with n vertices.
* @param n number of vertices. Should be >= 0. Return empty graph if n < 1.
* @param p probabilty of appearance of an edge. Should be between 0 and 1.
*/
static randomOGNP(n: number, p: number): Graph;
/**
* An acyclic tournament is a tournament which contains no directed cycle.
* Such a graph is a Directed Acyclic Graph (DAG).
* @param n number of vertices
*/
static acyclicTournament(n: number): Graph;
/**
* for every i < j, i -> j iff i+j is prime
* @param n
*/
static testTournament(n: number): Graph;
static generateAztecDiamond(n: number): Graph;
static generateGrid(n: number, m: number): Graph;
/**
* @returns an undirected cycle of length `n`.
*
* This graph has n vertices and n edges.
*/
static cycle(n: number): Graph;
static clique(n: number): Graph;
/**
* @param n is the number of vertices
*/
static path(n: number): Graph;
/**
* @param n is the number of vertices
* @returns an oriented path (`n` vertices and `n-1` edges)
*/
static orientedPath(n: number): Graph;
/**
* @param n is the number of vertices
* @returns an oriented cycle (`n` vertices and `n` edges)
*/
static orientedCycle(n: number): Graph;
/**
* Tournarment with n vertices such that v(i) -> v(i-j) for all j in [1,k]
* @param n number of vertices
* @param k order
* @example k = 0: acyclic
* @example k = 1: U-tournaments
*/
static UGtournament(n: number, k: number): Graph;
/**
* Return a random Unit Disk graph where vertices are set uniformely randomly in [-50,50]^2.
* @param n integer >= 0, the number of vertices
* @param d maximum distance between adjacent vertiecs
*/
static generateUnitDisk(n: number, d: number): Graph;
static randomTournament(n: number): Graph;
/**
* @param n
* @returns Star with n+1 vertices and n edges
*/
static star(n: number): Graph;
static generateIndependentCircle(n: number): Graph;
/**
* With Markov Chain strategy
* @param n number of vertices
* @returns a tree
*/
static randomTree(n: number): Graph;
static circulantTournament(n: number, gaps: Array<number>): Graph;
/**
* PaleyGraph is unoriented if p = 1 mod 4.
* It is oriented if p = -1 mod 4.
* @param p should be a prime number = +-1 mod 4
* @returns Error if p is not such a number
* @example undirected: 5 13 17, directed: 3 7 11
*/
static Paley(p: number): Graph;
/**
* The line graph is the graph associated to an undirected graph where the vertices are the edges of the initial graph.
* Two edges are adjacent in the line graph if they share a common endpoint.
* @returns
*/
static lineGraph(graph: Graph): Graph;
/**
* Return the geometric line graph is the graph whose vertices are the links of the initial graph.
* Two links are considered adjacent if the geometric paths intersect (they can intersect at their endpoints).
* Therefore the geometric line graph is a super graph of the line graph.
* @example for K4
* o---o
* |\ /| This K4 embedding
* | X | has one more edge in the geometric line graph
* |/ \|
* o---o
*
* @example
* o
* /|\
* / | \ This K4 embedding
* / o \ has the same geometric line graph and line graph
* /__/ \__\
* o---------o
*
*
*/
static geometricLineGraph(graph: Graph): Graph;
/**
* Returns an Undirected Graph from a list of edges represented by couples of indices.
* Weights are set to "".
*/
/**
* Return the list of the extremeties of the arcs.
*/
arcsList(): Array<[VertexIndex, VertexIndex]>;
/**
* WIP
* @returns
*/
hasLightExtension(): boolean;
/**
* A tournament is light if and only there does not exist different vertices u,v,a,b,c such that
* - u -> v
* - a -> b -> c -> a is an oriented triangle
* - v -> a,b,c
* - a,b,c -> u
* @returns undefined or a conflict [u,v,a,b,c]
*/
lightnessConflict(): Option<Array<Vertex>>;
/**
* A tournament is light if and only there does not exist different vertices u,v,a,b,c such that
* - u -> v
* - a -> b -> c -> a is an oriented triangle
* - v -> a,b,c
* - a,b,c -> u
* @remark If the graph is not light and you want to get 5 such vertices use the method `lightnessConflict()`
*/
isTournamentLight(): boolean;
/**
*
* @param link
* @returns false if
* - link is a loop
* - there is already a link with the same signature (same orientation and start and end)
*/
chekLink(link: Link): boolean;
getNeighborsListExcludingLinks(i: VertexIndex, excluded: Set<VertexIndex>): Array<VertexIndex>;
/**
*
* @param vId vertex
* @param d distance >= 0 (when d = 1, then it returns the closed in-neighborhood)
* @returns the closed in-neighborhood of `vId` at dist at most `d`. That is the vertices at distance at most d to `vId`.
* @example
* Graph.orientedPath(3).getClosedDistInNeighborhood(2, 2); // = [0,1,2]
* Graph.orientedPath(3).getClosedDistInNeighborhood(2, 1); // = [1,2]
*/
getClosedDistInNeighborhood(v: Vertex, d: number): Array<Vertex>;
/**
*
* @param vId vertex index
* @param d distance >= 0 (when d = 1, then it returns the closed in-neighborhood)
* @returns the closed in-neighborhood of `vId` at dist at most `d`. That is the vertices at distance at most d from `vId`.
* @example
* Graph.orientedPath(3).getClosedDistOutNeighborhood(0, 2); // = [0,1,2]
* Graph.orientedPath(3).getClosedDistOutNeighborhood(0, 1); // = [0,1]
*/
getClosedDistOutNeighborhood(v: Vertex, d: number): Array<Vertex>;
getDegreesData(): {
min_value: number;
min_vertices: null;
max_value: number;
max_vertices: null;
avg: number;
} | {
min_value: any;
min_vertices: Set<any>;
max_value: any;
max_vertices: Set<any>;
avg: number;
};
/**
* Return maximum (undirected) degree of the graph.
* Out-neighbors and In-neighbors are not taken in account.
* @returns -1 if there is no vertex
* @TODO should return undefined if no vertex
*/
maxDegree(): number;
/**
* Return the minimum (undirected) degree of the graph.
* Out-neighbors and In-neighbors are not considered.
* @returns Infinity if there is no vertex
*/
minDegree(): number;
/**
* Return minimum in-degree of the graph
* @returns `""` if there is no vertex
* @TODO replace string return by undefined
*/
minIndegree(): number | string;
/**
* Return maximum in-degree of the graph
* @returns undefined if there is no vertex
*/
maxIndegree(): number | undefined;
/**
* Return maximum out-degree of the graph
* @returns undefined if there is no vertex
*/
maxOutdegree(): number | undefined;
minOutdegree(): number | string;
DFSrecursive(v: Vertex, visited: Map<VertexIndex, boolean>): void;
DFSiterative(v: Vertex): Map<any, any>;
hasCycle(): boolean;
/**
* @returns [b, cycle] where b is a boolean which is true iff there exists a cycle.
* If b is true, a cycle is returned.
* @remark Iterative version
*/
hasCycle2(): [boolean, Array<Vertex>];
/**
*
* @returns girth of the graph. It is the minimum length of a cycle.
* If there is no cycle then `Infinity` is returned.
* If there is a cycle, a list of its consecutive vertices is returned.
*
* @example
* Graph.generateClique(4).girth() == 3
* Graph.generatePaley(13).girth() == 3
* Graph.petersen().girth() == 5
* Graph.star(3).girth() == 0
*/
girth(): number;
/**
* @returns a shortest cycle. It is a cycle of minimum length.
* Returns an empty array if there is no cycle.
*
* @example
* Graph.generateClique(4).shortestCycle().length == 3
* Graph.generatePaley(13).shortestCycle().length == 3
* Graph.petersen.shortestCycle().length == 5
* Graph.star(3).shortestCycle().length == 0
*/
shortestCycle(): Array<Vertex>;
/**
* @returns [b, cycle] where b is a boolean which is true iff there exists a directed cycle.
* If b is true, a directed cycle is returned.
* @remark Iterative version
*/
getDirectedCycle(): undefined | Array<VertexIndex>;
/**
* @returns [b, cycle] where b is a boolean which is true iff there exists a directed cycle.
* If b is true, a directed cycle is returned.
* @remark Recursive version
*/
hasDirectedCycleRecursive(): boolean;
sizeConnectedComponentOf(u: Vertex | VertexIndex): number;
sizeConnectedComponentExcludingLinks(vindex: VertexIndex, excluded: Set<VertexIndex>): number;
/**
* Return a cut edge which maximizes the minimum of the size of the connected components of its endvertices.
* Return -1 if there is no cut edge
*/
maxCutEdge(): number;
/**
* An undirected graph is said to be connected if there is a path between any pair of vertices.
* @returns true iff the graph is connected
*/
isConnected(): boolean;
/**
* A strongly connected component is a subset of vertices ot the graph such that for any pair (v,w) of vertices in this subset, there is a directed path from v to w.
* @returns the strongly connected components. By denoting `r` the returned array, then r[i] is an array containing all the vertices indices in the i-th strongly connected component.
* @algorithm Kosaraju's algorithm: https://en.wikipedia.org/wiki/Kosaraju's_algorithm
*/
stronglyConnectedComponents(): Array<Array<VertexIndex>>;
/**
* Compute a clique with a greedy algorithm.
*/
greedyClique(): Set<Vertex>;
/**
* Return the index of a maximal degree vertex or `undefined` if there is no vertices.
*/
maximalDegreeVertex(): Option<Vertex>;
/**
* Return true if the current coloring can be completed
*/
private auxChroma;
/**
* Returns the chromatic number of the graph.
* The chromatic number is the minimum integer k such that there exists a proper coloring with k colors.
* TODO: What happens with arcs? I dont know.
* Algorithm: Backtrack
* @param cliques is a set of cliques (sets of vIndices) of the graph. There are used to cut the search. The best would be to give the set of maximal cliques but it is NP-hard to compute. So if your graph has known cliques it is better.
*/
chromaticNumber(cliques?: Set<Set<Vertex>>): number;
/**
* Return a miniaml proper coloring.
* A coloring is proper if any two adjacent vertices have different colors.
* The chromatic number is the size of a minimal proper coloring.
* @param cliques is a set of cliques (sets of vIndices) of the graph. There are used to cut the search.
*/
minimalProperColoring(cliques?: Set<Set<Vertex>>): Map<VertexIndex, number>;
/**
* The dichromatic number is the minimum k such that if the vertices are colored with k colors, then the subgraph induced by each color is acyclic (there is no directed cycle)
* @returns
*
* What happens if a graph has a directed loop?
*
*/
minimumProperDicoloring(): Array<number>;
/**
* The dichromatic number is the minimum k such that if the vertices are colored with k colors, then the subgraph induced by each color is acyclic (there is no directed cycle)
* @returns
*
* What happens if a graph has a directed loop?
*
*/
dichromaticNumber(): number;
/**
* Undirected Feedback Vertex Set (UFVS)
*
* UNDER DEVELOPPEMENT
*/
private auxUFVS;
/**
* UNDER DEVELOPPEMENT
*/
private ufvsLauncher;
/**
* # Feedback Vertex Set Number
* A subset X of V(G) is a Feedback Vertex Set if G-X (deletion of vertices) is acyclic.
* The Feedback Vertex Set Number (FVSN) is the minimum size of a FVS.
* @returns the FVSN
* @example fvsn(directed cycle 3) = 1
*/
fvsn(): number;
/**
* OSBOLETE by V4
* Return true if the selection of vertices can be completed to a vertex cover of size at most k.
* @param k a nonnegative integer
* @param selection the vertices currently selection
*/
private auxVertexCoverV2K;
/**
* OSBOLETE by V4
* @param k
* @returns
*/
private vertexCoverV2K;
/**
* OSBOLETE by V4
* @param k
* @returns
*/
private auxVertexCover3;
/**
* OSBOLETE by V4
*/
private vertexCoverV3;
/**
* OSBOLETE by V4
*/
private auxVertexCover3Bis;
/**
* OSBOLETE by V4
*/
privatevertexCoverV3Bis(): number;
private auxVertexCover4;
private vertexCoverV4launcher;
private vertexCoverV4;
private vertexCoverV2;
/**
* Return a 2-approximation of the vertex number of the subgraphLinks
* @param subgraphLinks
* @returns
*/
vertexCover2Approx(subgraphLinks: Array<Link>): number;
generateRandomMaximalSubMatching(subgraphLinks: Array<Link>): number;
generateRandomMaximalMatching(): Array<Link>;
/**
* Compute the vertex cover number of the graph.
* It is the minimum integer k such that there exists a subset X of the vertices which is of size k and such that every edge is incident to at least one vertex of X.
* TODO: optional parameter m: asserts that the result it at least m
* TODO: return a certificate that it has a k-vertex-cover
* ALGO: uses kernelization and branch
*/
vertexCoverNumber(): number;
minVertexCover(): Set<VertexIndex>;
/**
* Return the maximum size of a clique extending `clique` if it is higher than record.
* @param commonNeighbors is the set of commonNeighbors of clique. Therefore any vertex extending the clique should be taken in this set.
* @param neighbors is the map of all the neighbors of every vertex.
* @param currentMaximumClique It is immediately updated by the size of the clique.
*/
private auxCliqueNumber;
/**
* Compute the clique number of the graph.
* It is the maximum size of a clique of the graph.
* @param cliqueSample is a clique known before computation
*/
cliqueNumber(cliqueSample?: Set<number>): number;
/**
* Compute a maximum clique of the graph.
* @param cliqueSample is a clique known before computation
*/
maximumClique(cliqueSample?: Set<VertexIndex>): Set<VertexIndex>;
/**
* @param subset the subset that we try to extend to a dominating set
* @param toDominate the vertices that remain to dominate
* @param choosable the vertices that can be added to the subset
* @param currentMin the current min dominating set
* @param variant depending on this variant, the final check of a branch leads to a solution or not. Better algorithms exist for these variants.
* @param lowerBound a lower bound on any minimum dominating set
* @returns a better dominating set than currentMin
*/
private auxMinDominatingSet;
private auxMinConnectedDominatingSet;
/**
* @returns a distance-2 independent set
* @algorithm random
* @example
* Graph.path(5).greedyLowerBoundDS().size; // = 2
* @todo make it greedy by taking vertices that have the lowest degrees
*/
private greedyLowerBoundDS;
/**
* @returns a minimum connected dominating set if the graph is connected: it is a connected subset X of the vertices such that all vertices of the graph are in X or adjacent to a vertex of X.
* @returns undefined if the graph is not connected
* @example
* Graph.generatePath(5).minConnectedDominatingSet().size; // = 3
* Graph.generateClique(3).minConnectedDominatingSet().size; // = 1
*/
minConnectedDominatingSet(): Set<Vertex> | undefined;
/**
* @returns a minimum dominating set: it is a subset X of the vertices such that all vertices of the graph are in X or adjacent to a vertex of X.
* @example
* Graph.generatePath(5).minDominatingSet().size; // = 2
*/
minDominatingSet(variant: undefined | DominationVariant): Set<Vertex>;
/**
* @returns the size of a minimum dominating set
* @example Graph.petersen().dominationNumber(); // = 3
*/
dominationNumber(): number;
/**
* @returns the size of a minimum independent dominating set
* @example Graph.petersen().independentDominationNumber(); // = 3
*/
independentDominationNumber(): number;
/**
* @returns the size of a minimum connected dominating set if the graph is connected
* @returns undefined if the graph is not connected
* @example
* Graph.generatePath(5).connectedDominationNumber(); // = 3
* Graph.petersen().connectedDominationNumber(); // = 4
*/
connectedDominationNumber(): number | undefined;
/**
* @returns an oriented distance-2 independent set
* @algorithm random
* @example
* Graph.path(5).greedyLowerBoundDS().size; // = 2
* @todo make it greedy by taking vertices that have the lowest degrees
*/
greedyOrientedDist2IndepSet(): Set<Vertex>;
/**
* DFVS: a subset X of the vertices such that all cycles contain a vertex of this subset.
* Equivalently: G-X has no cycle
* Equivalently: G-X is a DAG
*/
minDirectedFeedbackVertexSet(): Set<VertexIndex>;
/**
*
* @returns the minimum size of a DFVS
* @example
* Graph.orientedPath(5).directedFeedbackVertexSetNumber() == 0;
* Graph.orientedCycle(3).directedFeedbackVertexSetNumber() == 1;
* Graph.paley(7).directedFeedbackVertexSetNumber() == 4;
*/
directedFeedbackVertexSetNumber(): number;
/**
* Returns true if the graph is bipartite.
* A graph is said to be bipartite if it is 2-colorable.
* TODO: optimize
*/
isBipartite(): boolean;
/**
* Return true if is is possible to complete the positions of vertices so that dw(v) <= alpha(v) for every vertex.
*
* NOTE: there is a ruin of cut strategy, but it was not so much better.
* For each vertex x,
* if the number of vertices v such that indegree(v,x) > alpha(v)
* (where indegree(v,x) is the number of inneighbor of v which are outneighbors of x)
* is bigger than alpha(x), then it is not possible to complete the positions.
* Thus cut there.
* The PROBLEM is that it is too long to compute.
*
* IDEA: to improve the algorithm, use a matrix for the adjacency
* and a stack (or a set) for the vertices that remain to position
*
* @param i the number of vertices already positioned
* @param outNeighbors not mutated
* @param pos the position of the vertices in the order
* @param indegree the indegree array
* @param alpha the alpha array
* @returns
*/
private auxDegreewidth;
/**
* Return [dw, ordering] where dw is the degreewidth
* and ordering is an optimal ordering of the vertices.
*/
degreewidth(): [number, Array<Vertex>];
setLinkCp(index: number, cp: Option<Coord>): void;
/**
* Resets the edges of the graph so that they respect the Delaunay adjacency rule.
*/
resetDelaunayGraph(): void;
isDrawingPlanar(): boolean;
/**
* A crossing is pair of links whose curves intersect.
* @param stopAtFirst if a crossing is detected then it is immediately returned
* @returns a list of the indices of the crossings
* @todo improve bezier curve intersection
*/
crossings(stopAtFirst?: boolean): Array<[number, number]>;
/**
* @returns the number of crossings of the graph.
*/
crossingNumber(): number;
/**
* Subdivide links from linksIndices into k+1 links (rectilinear, with the same color, weight = "").
* @param linksIndices
* @param k >= 1, number of vertices inserted between each links.
*/
subdivideLinks(k: number, linksIndices?: Set<number>): void;
/**
* for every vertex of vertices_indices
* add arcs between these vertices according to their x-coordinate
*/
completeSubgraphIntoTournament(vertices_indices: Iterable<number>, arcDefault: (index: number, startVertex: Vertex, endVertex: Vertex) => Link): void;
/**
* The radius of the graph is the minimum `d` such that there exists a vertex v such that for every vertex w, d(v,w) <= d.
* Such a v is called a center.
* @returns [radius, centerIndex]
*/
radius(weights: undefined | Map<number, number>): [number, VertexIndex];
/**
* @returns the distances between each pair of vertices.
* @next next.get(u).get(v) is the adjacent vertex of u to go to v.
* It uses the algorithm of Floyd-Warshall.
* @param weights if undefined, the weigth of an edge `e` is 1 if `e.weight == ""` or `parseFloat(e.weight)`. Otherwise use the values of the map `weights` for the weights.
*/
FloydWarshall(weights: undefined | Map<number, number>): {
distances: Map<VertexIndex, Map<VertexIndex, number>>;
next: Map<VertexIndex, Map<VertexIndex, VertexIndex>>;
};
/**
* @returns a geodesic (or shortest path) of maximal weight and its weight
* @param weights if provided, it tells the shortest path computer to use the given weights. Otherwise it will use the weights of the links or 1 if a link has no weight.
* @example Graph.cycle(5).longestGeodesic(undefined)[1]; // == 2
*/
longestGeodesic(weights: undefined | Map<number, number>): [Array<VertexIndex>, number];
/**
*
* @returns the diameter of the graph which is the maximal length (or the maximal weight if weights are considered) of a shortest path in the graph.
* @param weights if provided, it tells the shortest path computer to use the given weights. Otherwise it will use the weights of the links or 1 if a link has no weight.
* @example Graph.cycle(5).diameter(); // == 2
* @example Graph.clique(4).diameter(); // == 1
*/
diameter(weights: undefined | Map<number, number>): number;
/**
* Compute a minimum spanning tree using Kruskal algorithm
* https://en.wikipedia.org/wiki/Kruskal%27s_algorithm.
* Return the weight of the spanning tree and the list of the edges of the tree.
* @note if weight of edge is "" or cannot be parsed into a float, then weight is set to 1
* @note it only considers edges
*/
minimumSpanningTree(): [number, Array<Link>];
/**
* Sets all weights of links to the euclidian distance between vertices.
* TODO: generalize to other distances.
*/
setEuclidianLinkWeights(): void;
/**
* Returns the stretch of the graph.
* The stretch is defined as the maximal stretch between pairs of vertices.
* The stretch of a pair of vertices is defined as the ratio between the euclidian distance in the graph between them and the euclidian distance between them.
* @returns 1 if there is 1 vertex or less and the shortestPath maximizing the stretch.
*/
stretch(): [number, Array<number>];
}