import { intersect } from "./set"
|
|
|
|
export type Graph<V> = {
|
|
numVertices: number,
|
|
numEdges: number,
|
|
vertices: {
|
|
[v: string]: V
|
|
},
|
|
edges: {
|
|
[v: string]: Set<string>
|
|
}
|
|
}
|
|
|
|
export type AddVertex<V> = (name: string, v: V) => void
|
|
export type AddEdge = (source: string, dest: string) => void
|
|
|
|
export const createGraph = <V>(cons: (v: AddVertex<V>, e: AddEdge) => void): Graph<V> => {
|
|
const g: Graph<V> = { numVertices: 0, numEdges: 0, vertices: {}, edges: {} }
|
|
|
|
const addVertex = (name: string, v: V) => {
|
|
if (typeof g.vertices[name] !== "undefined") {
|
|
return
|
|
}
|
|
|
|
g.vertices[name] = v
|
|
g.edges[name] = new Set()
|
|
g.numVertices++
|
|
}
|
|
|
|
const addEdge = (source: string, dest: string) => {
|
|
if (typeof g.vertices[source] === "undefined") {
|
|
throw new Error(`vertex \`${source}' does not exist in graph`)
|
|
} else if (typeof g.vertices[dest] === "undefined") {
|
|
throw new Error(`vertex \`${dest}' does not exist in graph`)
|
|
}
|
|
|
|
g.edges[source].add(dest)
|
|
g.edges[dest].add(source)
|
|
g.numEdges++
|
|
}
|
|
|
|
cons(addVertex, addEdge)
|
|
|
|
return g
|
|
}
|
|
|
|
export const neighbors = <V>(g: Graph<V>, name: string): Set<string> => {
|
|
if (typeof g.vertices[name] === "undefined") {
|
|
throw new Error(`vertex \`${name}' does not exist in graph`)
|
|
}
|
|
|
|
return g.edges[name]
|
|
}
|
|
|
|
export const vertices = <V>(g: Graph<V>): Set<string> => new Set(Object.keys(g.vertices))
|
|
|
|
export const edgeConnects = <V>(g: Graph<V>, v1: string, v2: string): boolean => g.edges[v1].has(v2)
|
|
|
|
export const maxCardinalitySearch = <V>(g: Graph<V>): Array<string> => {
|
|
const weights: { [s: string]: number } = {}
|
|
const ordering: Array<string> = []
|
|
|
|
const W = vertices(g)
|
|
const numVertices = W.size
|
|
|
|
for (let i = 0; i < numVertices; ++i) {
|
|
const v = findMaxWeight(weights, W)
|
|
ordering.push(v)
|
|
|
|
intersect(W, neighbors(g, v)).forEach(x =>
|
|
weights[x] = (weights[x] || 0) + 1
|
|
)
|
|
|
|
W.delete(v)
|
|
}
|
|
|
|
return ordering
|
|
}
|
|
|
|
const findMaxWeight = (weights: { [s: string]: number }, W: Set<string>): string => {
|
|
let maxV = null
|
|
let maxWeight = -Infinity
|
|
|
|
W.forEach(v => {
|
|
const vWeight = weights[v] || 0
|
|
if (vWeight > maxWeight) {
|
|
maxV = v
|
|
maxWeight = vWeight
|
|
}
|
|
})
|
|
|
|
if (maxV === null) {
|
|
throw new Error(`remaining vertex set is empty`)
|
|
}
|
|
|
|
return maxV
|
|
}
|
|
|
|
export type GraphColoring = {
|
|
numColors: number,
|
|
colors: VertexColoring,
|
|
}
|
|
|
|
export type VertexColoring = {
|
|
[s: string]: number,
|
|
}
|
|
|
|
export const colorGreedy = <V>(g: Graph<V>, vertexOrdering: Array<string>): GraphColoring => {
|
|
if (vertexOrdering.length !== g.numVertices) {
|
|
throw new Error("ordering does not cover all vertices")
|
|
}
|
|
|
|
const coloring: GraphColoring = { numColors: 0, colors: {} }
|
|
const usedColors: { [c: number]: boolean } = {}
|
|
|
|
vertexOrdering.forEach(v => {
|
|
const ns = neighbors(g, v)
|
|
|
|
let color = 0
|
|
while (!colorAvailable(color, ns, coloring.colors)) {
|
|
color++
|
|
}
|
|
|
|
coloring.colors[v] = color
|
|
usedColors[color] = true
|
|
})
|
|
|
|
coloring.numColors = Object.keys(usedColors).length
|
|
return coloring
|
|
}
|
|
|
|
const colorAvailable = (color: number, neighbors: Set<string>, colors: VertexColoring): boolean => {
|
|
// TODO: Why can't I just iterate over neighbors directly...?
|
|
for (const nbor of Array.from(neighbors)) {
|
|
if (colors[nbor] === color) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|