Skip to content

Commit

Permalink
feat(adjacency): update AdjacencyList
Browse files Browse the repository at this point in the history
- add vertices() iterator
- rename old `.vertices` field => `.adjacency`
- add adjListFromAdjacency() factory fn
  • Loading branch information
postspectacular committed Jul 19, 2022
1 parent 2f6e654 commit 5d85d87
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 40 deletions.
8 changes: 4 additions & 4 deletions packages/adjacency/src/binary.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { BitMatrix } from "@thi.ng/bitfield/bitmatrix";
import type { DegreeType, Edge, IGraph } from "./api.js";
import { into, invert, toDot } from "./utils.js";
import { __into, __invert, __toDot } from "./utils.js";

/**
* Adjacency matrix representation for both directed and undirected graphs and
Expand All @@ -17,7 +17,7 @@ export class AdjacencyBitMatrix implements IGraph<number> {
this.mat = new BitMatrix(n);
this.undirected = undirected;
this.numE = 0;
edges && into(this, edges);
edges && __into(this, edges);
}

*edges() {
Expand Down Expand Up @@ -100,7 +100,7 @@ export class AdjacencyBitMatrix implements IGraph<number> {
}

invert(): AdjacencyBitMatrix {
return invert(
return __invert(
new AdjacencyBitMatrix(this.mat.n, undefined, this.undirected),
this.edges()
);
Expand All @@ -111,7 +111,7 @@ export class AdjacencyBitMatrix implements IGraph<number> {
}

toDot(ids?: string[]) {
return toDot(this.edges(), this.undirected, ids);
return __toDot(this.edges(), this.undirected, ids);
}
}

Expand Down
74 changes: 45 additions & 29 deletions packages/adjacency/src/list.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import type { Nullable } from "@thi.ng/api";
import type { DegreeType, Edge, IGraph } from "./api.js";
import { into, invert, toDot } from "./utils.js";
import { __into, __invert, __toDot } from "./utils.js";

export class AdjacencyList implements IGraph<number> {
vertices: number[][] = [];
adjacency: number[][] = [];
indegree: number[] = [];
protected numE = 0;
protected numV = 0;

constructor(edges?: Iterable<Edge>) {
edges && into(this, edges);
edges && __into(this, edges);
}

numEdges(): number {
Expand All @@ -19,10 +20,17 @@ export class AdjacencyList implements IGraph<number> {
return this.numV;
}

*vertices() {
const { adjacency } = this;
for (let i = 0, n = adjacency.length; i < n; i++) {
if (adjacency[i]) yield i;
}
}

*edges() {
const vertices = this.vertices;
for (let i = 0, n = vertices.length; i < n; i++) {
const vertex = vertices[i];
const { adjacency } = this;
for (let i = 0, n = adjacency.length; i < n; i++) {
const vertex = adjacency[i];
if (!vertex) continue;
for (let j of vertex) yield <Edge>[i, j];
}
Expand All @@ -33,22 +41,20 @@ export class AdjacencyList implements IGraph<number> {
}

removeVertex(id: number) {
const { vertices, indegree } = this;
const vertex = vertices[id];
const { adjacency, indegree } = this;
const vertex = adjacency[id];
if (!vertex) return false;
// remove outgoing
while (vertex.length) {
const to = vertex.pop()!;
indegree[to]--;
indegree[vertex.pop()!]--;
this.numE--;
}
delete vertices[id];
delete adjacency[id];
// remove incoming
for (let i = 0, n = vertices.length; i < n && indegree[id] > 0; i++) {
const vertex = this.vertices[i];
for (let i = 0, n = adjacency.length; i < n && indegree[id] > 0; i++) {
const vertex = adjacency[i];
if (!vertex) continue;
while (vertex.includes(id)) this.removeEdge(i, id);
if (!vertex.length) delete this.vertices[i];
}
this.numV--;
return true;
Expand All @@ -64,7 +70,7 @@ export class AdjacencyList implements IGraph<number> {
}

removeEdge(from: number, to: number) {
const vertex = this.vertices[from];
const vertex = this.adjacency[from];
if (vertex) {
const dest = vertex.indexOf(to);
if (dest >= 0) {
Expand All @@ -78,13 +84,13 @@ export class AdjacencyList implements IGraph<number> {
}

hasEdge(from: number, to: number) {
const vertex = this.vertices[from];
const vertex = this.adjacency[from];
return vertex ? vertex.includes(to) : false;
}

degree(id: number, type: DegreeType = "out") {
let degree = 0;
const vertex = this.vertices[id];
const vertex = this.adjacency[id];
if (vertex) {
if (type !== "in") degree += vertex.length;
if (type !== "out") degree += this.indegree[id];
Expand All @@ -93,20 +99,24 @@ export class AdjacencyList implements IGraph<number> {
}

neighbors(id: number): Iterable<number> {
return [...(this.vertices[id] || [])];
return [...(this.adjacency[id] || [])];
}

invert(): AdjacencyList {
return invert(new AdjacencyList(), this.edges());
return __invert(new AdjacencyList(), this.edges());
}

toDot(ids?: string[]) {
return __toDot(this.edges(), false, ids);
}

toString() {
const vertices = this.vertices;
const { adjacency } = this;
const res: string[] = [];
for (let i = 0, n = vertices.length; i < n; i++) {
if (vertices[i]) {
for (let i = 0, n = adjacency.length; i < n; i++) {
if (adjacency[i]) {
res.push(
`${i}: [${[...vertices[i]!]
`${i}: [${[...adjacency[i]!]
.sort((a, b) => a - b)
.join(", ")}]`
);
Expand All @@ -115,17 +125,23 @@ export class AdjacencyList implements IGraph<number> {
return res.join("\n");
}

toDot(ids?: string[]) {
return toDot(this.edges(), false, ids);
}

protected ensureVertexData(id: number) {
const vertex = this.vertices[id];
const vertex = this.adjacency[id];
if (vertex) return vertex;
this.numV++;
this.indegree[id] = 0;
return (this.vertices[id] = []);
return (this.adjacency[id] = []);
}
}

export const defAdjList = (edges?: Iterable<Edge>) => new AdjacencyList(edges);

export const adjListFromAdjacency = (src: Nullable<number[]>[]) => {
const res = new AdjacencyList();
for (let i = 0, n = src.length; i < n; i++) {
const v = src[i];
if (!v) continue;
for (let w of v) res.addEdge(i, w);
}
return res;
};
8 changes: 4 additions & 4 deletions packages/adjacency/src/sparse.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ensureIndex2 } from "@thi.ng/errors/out-of-bounds";
import { CSR } from "@thi.ng/sparse/csr";
import type { DegreeType, Edge, IGraph } from "./api.js";
import { into, invert, toDot } from "./utils.js";
import { __into, __invert, __toDot } from "./utils.js";

export class AdjacencyMatrix extends CSR implements IGraph<number> {
undirected: boolean;
Expand Down Expand Up @@ -75,7 +75,7 @@ export class AdjacencyMatrix extends CSR implements IGraph<number> {
}

invert(): AdjacencyMatrix {
return invert(
return __invert(
defAdjMatrix(this.m, undefined, this.undirected),
this.edges()
);
Expand Down Expand Up @@ -168,7 +168,7 @@ export class AdjacencyMatrix extends CSR implements IGraph<number> {
}

toDot(ids?: string[]) {
return toDot(this.edges(), this.undirected, ids);
return __toDot(this.edges(), this.undirected, ids);
}
}

Expand All @@ -195,6 +195,6 @@ export const defAdjMatrix = (
raw.cols,
undirected
);
edges && into(mat, edges);
edges && __into(mat, edges);
return mat;
};
6 changes: 3 additions & 3 deletions packages/adjacency/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { Pair } from "@thi.ng/api";
import type { Edge, IGraph } from "./api.js";

/** @internal */
export const toDot = (
export const __toDot = (
edges: Iterable<Pair<number, number>>,
undirected: boolean,
ids?: string[]
Expand All @@ -22,14 +22,14 @@ export const toDot = (
};

/** @internal */
export const into = (graph: IGraph, edges: Iterable<Edge>) => {
export const __into = (graph: IGraph, edges: Iterable<Edge>) => {
for (let e of edges) {
graph.addEdge(e[0], e[1]);
}
};

/** @internal */
export const invert = <T extends IGraph>(graph: T, edges: Iterable<Edge>) => {
export const __invert = <T extends IGraph>(graph: T, edges: Iterable<Edge>) => {
for (let e of edges) {
graph.addEdge(e[1], e[0]);
}
Expand Down

0 comments on commit 5d85d87

Please sign in to comment.