77 * @generated 2026-01-18T16:10:41.825Z
88 */
99
10+ import {
11+ buildAdjacencyList ,
12+ hasInducedSubgraph ,
13+ SUBGRAPH_PATTERNS
14+ } from "../algorithms/extraction/forbidden-subgraphs.js" ;
15+ import {
16+ hasAsteroidalTriple ,
17+ hasInducedCycle as checkInducedCycle ,
18+ isDistanceHereditary as checkDistanceHereditary
19+ } from "../validation/forbidden-subgraph-helpers.js" ;
1020import type {
1121 ATFree ,
1222 BullFree ,
@@ -15,181 +25,233 @@ import type {
1525 GemFree ,
1626 HHFree ,
1727 P5Free ,
18- WeaklyChordal } from "../generation/spec/forbidden_subgraph.js" ;
28+ WeaklyChordal
29+ } from "../generation/spec/forbidden_subgraph.js" ;
1930import type { AnalyzerGraph , ComputePolicy } from "./types.js" ;
2031
2132/**
22- * Compute P5Free property from graph structure.
23- * Contains no induced path on 5 vertices
33+ * Convert AnalyzerGraph to adjacency list format.
2434 *
2535 * @param g - Analyzer graph
26- * @param _policy - Computation policy (unused in generated analyzers)
27- * @returns P5Free with computed value
36+ * @returns Tuple of (vertexSet, edgeList)
37+ */
38+ const toAdjacencyFormat = ( g : AnalyzerGraph ) : [ Set < number > , Array < [ number , number ] > ] => {
39+ // Map vertex IDs to numbers
40+ const nodeIdMap = new Map < string , number > ( ) ;
41+ for ( const [ index , v ] of g . vertices . entries ( ) ) {
42+ nodeIdMap . set ( v . id , index ) ;
43+ }
44+
45+ // Create vertex set and edge list
46+ const vertexSet = new Set < number > ( g . vertices . map ( ( _ , index ) => index ) ) ;
47+ const edgeList : Array < [ number , number ] > = [ ] ;
48+
49+ for ( const edge of g . edges ) {
50+ if ( edge . endpoints . length === 2 ) {
51+ const [ srcId , tgtId ] = edge . endpoints ;
52+ const src = nodeIdMap . get ( srcId ) ;
53+ const tgt = nodeIdMap . get ( tgtId ) ;
54+ if ( src !== undefined && tgt !== undefined ) {
55+ edgeList . push ( [ src , tgt ] ) ;
56+ }
57+ }
58+ }
59+
60+ return [ vertexSet , edgeList ] ;
61+ } ;
62+
63+ /**
64+ * Build mutable adjacency map from AnalyzerGraph.
65+ *
66+ * @param g - Analyzer graph
67+ * @returns Adjacency map
68+ */
69+ const buildAdjacencyMap = ( g : AnalyzerGraph ) : Map < number , Set < number > > => {
70+ const [ vertexSet , edgeList ] = toAdjacencyFormat ( g ) ;
71+ const adj = new Map < number , Set < number > > ( ) ;
72+
73+ for ( const v of vertexSet ) {
74+ adj . set ( v , new Set ( ) ) ;
75+ }
76+
77+ for ( const [ u , v ] of edgeList ) {
78+ adj . get ( u ) ?. add ( v ) ;
79+ adj . get ( v ) ?. add ( u ) ;
80+ }
81+
82+ return adj ;
83+ } ;
84+
85+ /**
86+ * Compute P5Free property from graph structure.
87+ * Contains no induced path on 5 vertices
2888 */
2989export const computeP5Free = (
3090 g : AnalyzerGraph ,
3191 _policy : ComputePolicy
3292) : P5Free => {
33- // TODO: Implement analysis logic for P5Free
34- // For now, return placeholder value
35-
36- // Example: Check for forbidden subgraph
37- // const hasForbidden = checkForForbiddenSubgraph(g);
38- // return hasForbidden ? { kind: "has_p5" } : { kind: "p5_free" };
39-
40- return { kind : "p5_free" } ;
93+ const [ vertexSet , edgeList ] = toAdjacencyFormat ( g ) ;
94+ const adjacency = buildAdjacencyList ( vertexSet , edgeList ) ;
95+ const hasP5 = hasInducedSubgraph ( adjacency , SUBGRAPH_PATTERNS . P5 ) ;
96+ return hasP5 ? { kind : "has_p5" } : { kind : "p5_free" } ;
4197} ;
4298
4399/**
44100 * Compute C5Free property from graph structure.
45101 * Contains no induced cycle on 5 vertices
46- *
47- * @param g - Analyzer graph
48- * @param _policy - Computation policy (unused in generated analyzers)
49- * @returns C5Free with computed value
50102 */
51103export const computeC5Free = (
52104 g : AnalyzerGraph ,
53105 _policy : ComputePolicy
54106) : C5Free => {
55- // TODO: Implement analysis logic for C5Free
56- // For now, return placeholder value
57-
58- // Example: Check for forbidden subgraph
59- // const hasForbidden = checkForForbiddenSubgraph(g);
60- // return hasForbidden ? { kind: "has_c5" } : { kind: "c5_free" };
61-
62- return { kind : "c5_free" } ;
107+ const [ vertexSet , edgeList ] = toAdjacencyFormat ( g ) ;
108+ const adjacency = buildAdjacencyList ( vertexSet , edgeList ) ;
109+ const hasC5 = hasInducedSubgraph ( adjacency , SUBGRAPH_PATTERNS . C5 ) ;
110+ return hasC5 ? { kind : "has_c5" } : { kind : "c5_free" } ;
63111} ;
64112
65113/**
66114 * Compute BullFree property from graph structure.
67115 * Contains no induced bull graph
68- *
69- * @param g - Analyzer graph
70- * @param _policy - Computation policy (unused in generated analyzers)
71- * @returns BullFree with computed value
72116 */
73117export const computeBullFree = (
74118 g : AnalyzerGraph ,
75119 _policy : ComputePolicy
76120) : BullFree => {
77- // TODO: Implement analysis logic for BullFree
78- // For now, return placeholder value
79-
80- // Example: Check for forbidden subgraph
81- // const hasForbidden = checkForForbiddenSubgraph(g);
82- // return hasForbidden ? { kind: "has_bull" } : { kind: "bull_free" };
83-
84- return { kind : "bull_free" } ;
121+ const [ vertexSet , edgeList ] = toAdjacencyFormat ( g ) ;
122+ const adjacency = buildAdjacencyList ( vertexSet , edgeList ) ;
123+ const hasBull = hasInducedSubgraph ( adjacency , SUBGRAPH_PATTERNS . bull ) ;
124+ return hasBull ? { kind : "has_bull" } : { kind : "bull_free" } ;
85125} ;
86126
87127/**
88128 * Compute GemFree property from graph structure.
89129 * Contains no induced gem graph
90- *
91- * @param g - Analyzer graph
92- * @param _policy - Computation policy (unused in generated analyzers)
93- * @returns GemFree with computed value
94130 */
95131export const computeGemFree = (
96132 g : AnalyzerGraph ,
97133 _policy : ComputePolicy
98134) : GemFree => {
99- // TODO: Implement analysis logic for GemFree
100- // For now, return placeholder value
101-
102- // Example: Check for forbidden subgraph
103- // const hasForbidden = checkForForbiddenSubgraph(g);
104- // return hasForbidden ? { kind: "has_gem" } : { kind: "gem_free" };
105-
106- return { kind : "gem_free" } ;
107- } ;
108-
109- /**
110- * Compute WeaklyChordal property from graph structure.
111- * No hole or antihole of length 5 or more
112- *
113- * @param g - Analyzer graph
114- * @param _policy - Computation policy (unused in generated analyzers)
115- * @returns WeaklyChordal with computed value
116- */
117- export const computeWeaklyChordal = (
118- g : AnalyzerGraph ,
119- _policy : ComputePolicy
120- ) : WeaklyChordal => {
121- // TODO: Implement analysis logic for WeaklyChordal
122- // For now, return placeholder value
123-
124- // Example: Check for forbidden subgraph
125- // const hasForbidden = checkForForbiddenSubgraph(g);
126- // return hasForbidden ? { kind: "has_hole_or_antihole" } : { kind: "weakly_chordal" };
127-
128- return { kind : "weakly_chordal" } ;
135+ const [ vertexSet , edgeList ] = toAdjacencyFormat ( g ) ;
136+ const adjacency = buildAdjacencyList ( vertexSet , edgeList ) ;
137+ const hasGem = hasInducedSubgraph ( adjacency , SUBGRAPH_PATTERNS . gem ) ;
138+ return hasGem ? { kind : "has_gem" } : { kind : "gem_free" } ;
129139} ;
130140
131141/**
132142 * Compute ATFree property from graph structure.
133143 * No asteroidal triple of vertices
134- *
135- * @param g - Analyzer graph
136- * @param _policy - Computation policy (unused in generated analyzers)
137- * @returns ATFree with computed value
138144 */
139145export const computeATFree = (
140146 g : AnalyzerGraph ,
141147 _policy : ComputePolicy
142148) : ATFree => {
143- // TODO: Implement analysis logic for ATFree
144- // For now, return placeholder value
149+ const adj = buildAdjacencyMap ( g ) ;
150+ const vertices = Array . from ( adj . keys ( ) ) ;
145151
146- // Example: Check for forbidden subgraph
147- // const hasForbidden = checkForForbiddenSubgraph(g);
148- // return hasForbidden ? { kind: "has_asteroidal_triple" } : { kind: "at_free" };
152+ // Check all triples for asteroidal triples
153+ let hasAT = false ;
154+ for ( let i = 0 ; i < vertices . length && ! hasAT ; i ++ ) {
155+ for ( let j = i + 1 ; j < vertices . length && ! hasAT ; j ++ ) {
156+ for ( let k = j + 1 ; k < vertices . length && ! hasAT ; k ++ ) {
157+ if ( hasAsteroidalTriple ( adj , vertices [ i ] , vertices [ j ] , vertices [ k ] ) ) {
158+ hasAT = true ;
159+ }
160+ }
161+ }
162+ }
149163
150- return { kind : "at_free" } ;
164+ return hasAT ? { kind : "has_asteroidal_triple" } : { kind : "at_free" } ;
151165} ;
152166
153167/**
154168 * Compute HHFree property from graph structure.
155- * No induced house or hole
156- *
157- * @param g - Analyzer graph
158- * @param _policy - Computation policy (unused in generated analyzers)
159- * @returns HHFree with computed value
169+ * No induced house or hole (cycle of length >= 5)
160170 */
161171export const computeHHFree = (
162172 g : AnalyzerGraph ,
163173 _policy : ComputePolicy
164174) : HHFree => {
165- // TODO: Implement analysis logic for HHFree
166- // For now, return placeholder value
175+ const [ vertexSet , edgeList ] = toAdjacencyFormat ( g ) ;
176+ const adjacency = buildAdjacencyList ( vertexSet , edgeList ) ;
177+ const adj = buildAdjacencyMap ( g ) ;
178+
179+ // Check for house
180+ const hasHouse = hasInducedSubgraph ( adjacency , SUBGRAPH_PATTERNS . house ) ;
167181
168- // Example: Check for forbidden subgraph
169- // const hasForbidden = checkForForbiddenSubgraph(g);
170- // return hasForbidden ? { kind: "has_house_or_hole" } : { kind: "hh_free" };
182+ // Check for holes (cycles of length >= 5)
183+ let hasHole = false ;
184+ for ( let cycleLength = 5 ; cycleLength <= g . vertices . length ; cycleLength ++ ) {
185+ if ( checkInducedCycle ( adj , vertexSet , cycleLength ) ) {
186+ hasHole = true ;
187+ break ;
188+ }
189+ }
171190
172- return { kind : "hh_free" } ;
191+ return hasHouse || hasHole ? { kind : "has_house_or_hole" } : { kind : "hh_free" } ;
173192} ;
174193
175194/**
176195 * Compute DistanceHereditary property from graph structure.
177196 * Distances preserved in all connected induced subgraphs
178- *
179- * @param g - Analyzer graph
180- * @param _policy - Computation policy (unused in generated analyzers)
181- * @returns DistanceHereditary with computed value
182197 */
183198export const computeDistanceHereditary = (
184199 g : AnalyzerGraph ,
185200 _policy : ComputePolicy
186201) : DistanceHereditary => {
187- // TODO: Implement analysis logic for DistanceHereditary
188- // For now, return placeholder value
202+ const adj = buildAdjacencyMap ( g ) ;
203+ const vertexSet = new Set ( g . vertices . map ( ( _ , index ) => index ) ) ;
204+ const isDH = checkDistanceHereditary ( adj , vertexSet ) ;
205+ return isDH ? { kind : "distance_hereditary" } : { kind : "not_distance_hereditary" } ;
206+ } ;
207+
208+ /**
209+ * Compute WeaklyChordal property from graph structure.
210+ * No induced hole or anti-hole of length >= 5
211+ */
212+ export const computeWeaklyChordal = (
213+ g : AnalyzerGraph ,
214+ _policy : ComputePolicy
215+ ) : WeaklyChordal => {
216+ const [ vertexSet , edgeList ] = toAdjacencyFormat ( g ) ;
217+ const adj = buildAdjacencyMap ( g ) ;
218+
219+ // Check for holes (cycles of length >= 5)
220+ let hasHole = false ;
221+ for ( let cycleLength = 5 ; cycleLength <= g . vertices . length ; cycleLength ++ ) {
222+ if ( checkInducedCycle ( adj , vertexSet , cycleLength ) ) {
223+ hasHole = true ;
224+ break ;
225+ }
226+ }
227+
228+ // Check for antiholes (complement has hole)
229+ // Build complement adjacency
230+ const complementAdj = new Map < number , Set < number > > ( ) ;
231+ for ( const v of vertexSet ) {
232+ complementAdj . set ( v , new Set ( ) ) ;
233+ }
234+
235+ const vertices = Array . from ( vertexSet ) ;
236+ for ( let i = 0 ; i < vertices . length ; i ++ ) {
237+ for ( let j = i + 1 ; j < vertices . length ; j ++ ) {
238+ const u = vertices [ i ] ;
239+ const v = vertices [ j ] ;
240+ // Add edge in complement if not adjacent in original
241+ if ( ! adj . get ( u ) ?. has ( v ) ) {
242+ complementAdj . get ( u ) ?. add ( v ) ;
243+ complementAdj . get ( v ) ?. add ( u ) ;
244+ }
245+ }
246+ }
189247
190- // Example: Check for forbidden subgraph
191- // const hasForbidden = checkForForbiddenSubgraph(g);
192- // return hasForbidden ? { kind: "not_distance_hereditary" } : { kind: "distance_hereditary" };
248+ let hasAntihole = false ;
249+ for ( let cycleLength = 5 ; cycleLength <= g . vertices . length ; cycleLength ++ ) {
250+ if ( checkInducedCycle ( complementAdj , vertexSet , cycleLength ) ) {
251+ hasAntihole = true ;
252+ break ;
253+ }
254+ }
193255
194- return { kind : "distance_hereditary " } ;
256+ return hasHole || hasAntihole ? { kind : "has_hole_or_antihole" } : { kind : "weakly_chordal " } ;
195257} ;
0 commit comments