Skip to content

Commit 3247265

Browse files
committed
test: add integration tests for graph class roundtrip validation
add integration tests that verify generated graphs are correctly classified by their corresponding predicates: - tree: acyclic connected graphs - bipartite: two-colorable graphs - chordal: graphs with no induced cycles > 3 - interval: intersection graphs of intervals - split: clique + independent set partitions - cograph: p4-free graphs - comparability: transitively orientable graphs - permutation: intersection of two linear orders - perfect: ω(h) = χ(h) for all induced subgraphs - planar: embeddable without edge crossings - regular: all vertices same degree - eulerian: all vertices even degree - hamiltonian: has hamiltonian cycle
1 parent 0e6a530 commit 3247265

13 files changed

+1361
-0
lines changed
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
/**
2+
* Integration tests for Bipartite graph class
3+
*
4+
* Bipartite: vertices can be partitioned into two disjoint sets
5+
* such that every edge connects a vertex in one set to one in the other.
6+
* Equivalent: graph contains no odd-length cycles.
7+
*/
8+
9+
import { describe, expect, it } from "vitest";
10+
11+
import { isBipartite } from "../../analyzer";
12+
import { generateGraph } from "../../generation/generator";
13+
import { type GraphSpec,makeGraphSpec } from "../../generation/spec";
14+
import { toAnalyzerGraph } from "./helpers";
15+
16+
describe("Bipartite Graph Class", () => {
17+
describe("generation and classification roundtrip", () => {
18+
it("should generate and classify a bipartite graph correctly", () => {
19+
const spec: GraphSpec = {
20+
...makeGraphSpec({
21+
directionality: { kind: "undirected" },
22+
edgeMultiplicity: { kind: "simple" },
23+
selfLoops: { kind: "disallowed" },
24+
}),
25+
partiteness: { kind: "bipartite" },
26+
};
27+
28+
const testGraph = generateGraph(spec, { nodeCount: 10, seed: 42 });
29+
const analyzerGraph = toAnalyzerGraph(testGraph);
30+
31+
expect(isBipartite(analyzerGraph)).toBe(true);
32+
});
33+
34+
it("should classify bipartite graphs of various sizes", () => {
35+
const spec: GraphSpec = {
36+
...makeGraphSpec({
37+
directionality: { kind: "undirected" },
38+
edgeMultiplicity: { kind: "simple" },
39+
selfLoops: { kind: "disallowed" },
40+
}),
41+
partiteness: { kind: "bipartite" },
42+
};
43+
44+
for (const nodeCount of [4, 8, 12, 20]) {
45+
const testGraph = generateGraph(spec, { nodeCount, seed: nodeCount });
46+
const analyzerGraph = toAnalyzerGraph(testGraph);
47+
48+
expect(isBipartite(analyzerGraph)).toBe(true);
49+
}
50+
});
51+
52+
it("should have nodes partitioned into two sets", () => {
53+
const spec: GraphSpec = {
54+
...makeGraphSpec({
55+
directionality: { kind: "undirected" },
56+
edgeMultiplicity: { kind: "simple" },
57+
selfLoops: { kind: "disallowed" },
58+
}),
59+
partiteness: { kind: "bipartite" },
60+
};
61+
62+
const testGraph = generateGraph(spec, { nodeCount: 10, seed: 42 });
63+
64+
// Check that nodes have partition labels
65+
const leftNodes = testGraph.nodes.filter(n => n.partition === "left");
66+
const rightNodes = testGraph.nodes.filter(n => n.partition === "right");
67+
68+
expect(leftNodes.length + rightNodes.length).toBe(testGraph.nodes.length);
69+
expect(leftNodes.length).toBeGreaterThan(0);
70+
expect(rightNodes.length).toBeGreaterThan(0);
71+
});
72+
73+
it("should have edges only between partitions", () => {
74+
const spec: GraphSpec = {
75+
...makeGraphSpec({
76+
directionality: { kind: "undirected" },
77+
edgeMultiplicity: { kind: "simple" },
78+
selfLoops: { kind: "disallowed" },
79+
}),
80+
partiteness: { kind: "bipartite" },
81+
};
82+
83+
const testGraph = generateGraph(spec, { nodeCount: 10, seed: 42 });
84+
85+
const nodePartition = new Map<string, string>();
86+
for (const node of testGraph.nodes) {
87+
nodePartition.set(node.id, node.partition ?? "unknown");
88+
}
89+
90+
// Every edge should connect nodes in different partitions
91+
for (const edge of testGraph.edges) {
92+
const sourcePartition = nodePartition.get(edge.source);
93+
const targetPartition = nodePartition.get(edge.target);
94+
expect(sourcePartition).not.toBe(targetPartition);
95+
}
96+
});
97+
});
98+
99+
describe("complete bipartite graphs", () => {
100+
it("should generate complete bipartite graph K_{m,n}", () => {
101+
const spec: GraphSpec = {
102+
...makeGraphSpec({
103+
directionality: { kind: "undirected" },
104+
edgeMultiplicity: { kind: "simple" },
105+
selfLoops: { kind: "disallowed" },
106+
}),
107+
partiteness: { kind: "bipartite" },
108+
completeBipartite: { kind: "complete_bipartite", m: 4, n: 6 },
109+
};
110+
111+
const testGraph = generateGraph(spec, { nodeCount: 10, seed: 42 });
112+
const analyzerGraph = toAnalyzerGraph(testGraph);
113+
114+
expect(isBipartite(analyzerGraph)).toBe(true);
115+
116+
// Complete bipartite has m*n edges where m and n are partition sizes
117+
const leftNodes = testGraph.nodes.filter(n => n.partition === "left");
118+
const rightNodes = testGraph.nodes.filter(n => n.partition === "right");
119+
const expectedEdges = leftNodes.length * rightNodes.length;
120+
121+
expect(testGraph.edges.length).toBe(expectedEdges);
122+
});
123+
});
124+
125+
describe("deterministic generation", () => {
126+
it("should produce identical bipartite graphs with same seed", () => {
127+
const spec: GraphSpec = {
128+
...makeGraphSpec({
129+
directionality: { kind: "undirected" },
130+
edgeMultiplicity: { kind: "simple" },
131+
selfLoops: { kind: "disallowed" },
132+
}),
133+
partiteness: { kind: "bipartite" },
134+
};
135+
136+
const graph1 = generateGraph(spec, { nodeCount: 10, seed: 999 });
137+
const graph2 = generateGraph(spec, { nodeCount: 10, seed: 999 });
138+
139+
expect(graph1.edges).toEqual(graph2.edges);
140+
});
141+
});
142+
});
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/**
2+
* Integration tests for Chordal graph class
3+
*
4+
* Chordal: every cycle of length >= 4 has a chord (edge between non-adjacent vertices).
5+
* Equivalent: has a perfect elimination ordering.
6+
* Examples: trees, complete graphs, interval graphs.
7+
*/
8+
9+
import { describe, expect, it } from "vitest";
10+
11+
import { isChordal } from "../../analyzer";
12+
import { generateGraph } from "../../generation/generator";
13+
import { type GraphSpec,makeGraphSpec } from "../../generation/spec";
14+
import { toAnalyzerGraph } from "./helpers";
15+
16+
describe("Chordal Graph Class", () => {
17+
describe("generation and classification roundtrip", () => {
18+
it("should generate and classify a chordal graph correctly", () => {
19+
const spec: GraphSpec = {
20+
...makeGraphSpec({
21+
directionality: { kind: "undirected" },
22+
edgeMultiplicity: { kind: "simple" },
23+
selfLoops: { kind: "disallowed" },
24+
}),
25+
chordal: { kind: "chordal" },
26+
};
27+
28+
const testGraph = generateGraph(spec, { nodeCount: 10, seed: 42 });
29+
const analyzerGraph = toAnalyzerGraph(testGraph);
30+
31+
expect(isChordal(analyzerGraph)).toBe(true);
32+
});
33+
34+
it("should classify chordal graphs of various sizes", () => {
35+
const spec: GraphSpec = {
36+
...makeGraphSpec({
37+
directionality: { kind: "undirected" },
38+
edgeMultiplicity: { kind: "simple" },
39+
selfLoops: { kind: "disallowed" },
40+
}),
41+
chordal: { kind: "chordal" },
42+
};
43+
44+
for (const nodeCount of [4, 8, 12, 16]) {
45+
const testGraph = generateGraph(spec, { nodeCount, seed: nodeCount });
46+
const analyzerGraph = toAnalyzerGraph(testGraph);
47+
48+
expect(isChordal(analyzerGraph)).toBe(true);
49+
}
50+
});
51+
});
52+
53+
describe("trees are chordal", () => {
54+
it("should classify trees as chordal (trees have no cycles)", () => {
55+
const spec = makeGraphSpec({
56+
directionality: { kind: "undirected" },
57+
cycles: { kind: "acyclic" },
58+
connectivity: { kind: "connected" },
59+
edgeMultiplicity: { kind: "simple" },
60+
selfLoops: { kind: "disallowed" },
61+
});
62+
63+
const testGraph = generateGraph(spec, { nodeCount: 10, seed: 42 });
64+
const analyzerGraph = toAnalyzerGraph(testGraph);
65+
66+
// Trees are trivially chordal (no cycles at all)
67+
expect(isChordal(analyzerGraph)).toBe(true);
68+
});
69+
});
70+
71+
describe("complete graphs are chordal", () => {
72+
it("should classify complete graphs as chordal", () => {
73+
const spec = makeGraphSpec({
74+
directionality: { kind: "undirected" },
75+
completeness: { kind: "complete" },
76+
edgeMultiplicity: { kind: "simple" },
77+
selfLoops: { kind: "disallowed" },
78+
});
79+
80+
const testGraph = generateGraph(spec, { nodeCount: 6, seed: 42 });
81+
const analyzerGraph = toAnalyzerGraph(testGraph);
82+
83+
// Complete graphs are chordal (every possible chord exists)
84+
expect(isChordal(analyzerGraph)).toBe(true);
85+
});
86+
});
87+
88+
describe("deterministic generation", () => {
89+
it("should produce identical chordal graphs with same seed", () => {
90+
const spec: GraphSpec = {
91+
...makeGraphSpec({
92+
directionality: { kind: "undirected" },
93+
edgeMultiplicity: { kind: "simple" },
94+
selfLoops: { kind: "disallowed" },
95+
}),
96+
chordal: { kind: "chordal" },
97+
};
98+
99+
const graph1 = generateGraph(spec, { nodeCount: 10, seed: 999 });
100+
const graph2 = generateGraph(spec, { nodeCount: 10, seed: 999 });
101+
102+
expect(graph1.edges).toEqual(graph2.edges);
103+
});
104+
});
105+
});
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/**
2+
* Integration tests for Cograph graph class
3+
*
4+
* Cograph: P4-free graph (contains no induced path on 4 vertices).
5+
* Can be constructed recursively using disjoint union and complement operations.
6+
*/
7+
8+
import { describe, expect, it } from "vitest";
9+
10+
import { isCograph } from "../../analyzer";
11+
import { generateGraph } from "../../generation/generator";
12+
import { type GraphSpec,makeGraphSpec } from "../../generation/spec";
13+
import { toAnalyzerGraph } from "./helpers";
14+
15+
describe("Cograph Graph Class", () => {
16+
describe("generation and classification roundtrip", () => {
17+
it("should generate and classify a cograph correctly", () => {
18+
const spec: GraphSpec = {
19+
...makeGraphSpec({
20+
directionality: { kind: "undirected" },
21+
edgeMultiplicity: { kind: "simple" },
22+
selfLoops: { kind: "disallowed" },
23+
}),
24+
cograph: { kind: "cograph" },
25+
};
26+
27+
const testGraph = generateGraph(spec, { nodeCount: 10, seed: 42 });
28+
const analyzerGraph = toAnalyzerGraph(testGraph);
29+
30+
expect(isCograph(analyzerGraph)).toBe(true);
31+
});
32+
33+
it("should classify cographs of various sizes", () => {
34+
const spec: GraphSpec = {
35+
...makeGraphSpec({
36+
directionality: { kind: "undirected" },
37+
edgeMultiplicity: { kind: "simple" },
38+
selfLoops: { kind: "disallowed" },
39+
}),
40+
cograph: { kind: "cograph" },
41+
};
42+
43+
for (const nodeCount of [4, 8, 12, 16]) {
44+
const testGraph = generateGraph(spec, { nodeCount, seed: nodeCount });
45+
const analyzerGraph = toAnalyzerGraph(testGraph);
46+
47+
expect(isCograph(analyzerGraph)).toBe(true);
48+
}
49+
});
50+
});
51+
52+
describe("complete graphs are cographs", () => {
53+
it("should classify complete graphs as cographs", () => {
54+
const spec = makeGraphSpec({
55+
directionality: { kind: "undirected" },
56+
completeness: { kind: "complete" },
57+
edgeMultiplicity: { kind: "simple" },
58+
selfLoops: { kind: "disallowed" },
59+
});
60+
61+
const testGraph = generateGraph(spec, { nodeCount: 6, seed: 42 });
62+
const analyzerGraph = toAnalyzerGraph(testGraph);
63+
64+
// Complete graphs are cographs (no P4 possible)
65+
expect(isCograph(analyzerGraph)).toBe(true);
66+
});
67+
});
68+
69+
describe("deterministic generation", () => {
70+
it("should produce identical cographs with same seed", () => {
71+
const spec: GraphSpec = {
72+
...makeGraphSpec({
73+
directionality: { kind: "undirected" },
74+
edgeMultiplicity: { kind: "simple" },
75+
selfLoops: { kind: "disallowed" },
76+
}),
77+
cograph: { kind: "cograph" },
78+
};
79+
80+
const graph1 = generateGraph(spec, { nodeCount: 10, seed: 999 });
81+
const graph2 = generateGraph(spec, { nodeCount: 10, seed: 999 });
82+
83+
expect(graph1.edges).toEqual(graph2.edges);
84+
});
85+
});
86+
});

0 commit comments

Comments
 (0)