• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

graphty-org / graphty-element / 20514590651

26 Dec 2025 02:37AM UTC coverage: 70.559% (-0.3%) from 70.836%
20514590651

push

github

apowers313
ci: fix npm ci

9591 of 13363 branches covered (71.77%)

Branch coverage included in aggregate %.

25136 of 35854 relevant lines covered (70.11%)

6233.71 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.43
/src/algorithms/LouvainAlgorithm.ts
1
import {louvain} from "@graphty/algorithms";
15✔
2
import {z} from "zod/v4";
15✔
3

4
import {defineOptions, type OptionsSchema as ZodOptionsSchema, type SuggestedStylesConfig} from "../config";
15✔
5
import {Algorithm} from "./Algorithm";
15✔
6
import type {OptionsSchema} from "./types/OptionSchema";
7
import {toAlgorithmGraph} from "./utils/graphConverter";
15✔
8

9
/**
10
 * Zod-based options schema for Louvain algorithm
11
 */
12
export const louvainOptionsSchema = defineOptions({
15✔
13
    resolution: {
15✔
14
        schema: z.number().min(0.1).max(5.0).default(1.0),
15✔
15
        meta: {
15✔
16
            label: "Resolution",
15✔
17
            description: "Higher = more communities, lower = fewer larger communities",
15✔
18
            step: 0.1,
15✔
19
        },
15✔
20
    },
15✔
21
    maxIterations: {
15✔
22
        schema: z.number().int().min(1).max(500).default(100),
15✔
23
        meta: {
15✔
24
            label: "Max Iterations",
15✔
25
            description: "Maximum optimization iterations",
15✔
26
            advanced: true,
15✔
27
        },
15✔
28
    },
15✔
29
    tolerance: {
15✔
30
        schema: z.number().min(1e-10).max(0.01).default(1e-6),
15✔
31
        meta: {
15✔
32
            label: "Tolerance",
15✔
33
            description: "Minimum modularity improvement to continue",
15✔
34
            advanced: true,
15✔
35
        },
15✔
36
    },
15✔
37
    useOptimized: {
15✔
38
        schema: z.boolean().default(true),
15✔
39
        meta: {
15✔
40
            label: "Use Optimized",
15✔
41
            description: "Use optimized implementation for better performance on large graphs",
15✔
42
            advanced: true,
15✔
43
        },
15✔
44
    },
15✔
45
});
15✔
46

47
/**
48
 * Options for the Louvain community detection algorithm
49
 */
50
export interface LouvainOptions extends Record<string, unknown> {
51
    /** Higher = more communities, lower = fewer larger communities */
52
    resolution: number;
53
    /** Maximum optimization iterations */
54
    maxIterations: number;
55
    /** Minimum modularity improvement to continue */
56
    tolerance: number;
57
    /** Use optimized implementation for better performance on large graphs */
58
    useOptimized: boolean;
59
}
60

61
/**
62
 *
63
 */
64
export class LouvainAlgorithm extends Algorithm<LouvainOptions> {
15✔
65
    static namespace = "graphty";
19✔
66
    static type = "louvain";
120✔
67

68
    static zodOptionsSchema: ZodOptionsSchema = louvainOptionsSchema;
120✔
69

70
    static optionsSchema: OptionsSchema = {
120✔
71
        resolution: {
120✔
72
            type: "number",
120✔
73
            default: 1.0,
120✔
74
            label: "Resolution",
120✔
75
            description: "Higher = more communities, lower = fewer larger communities",
120✔
76
            min: 0.1,
120✔
77
            max: 5.0,
120✔
78
            step: 0.1,
120✔
79
        },
120✔
80
        maxIterations: {
120✔
81
            type: "integer",
120✔
82
            default: 100,
120✔
83
            label: "Max Iterations",
120✔
84
            description: "Maximum optimization iterations",
120✔
85
            min: 1,
120✔
86
            max: 500,
120✔
87
            advanced: true,
120✔
88
        },
120✔
89
        tolerance: {
120✔
90
            type: "number",
120✔
91
            default: 1e-6,
120✔
92
            label: "Tolerance",
120✔
93
            description: "Minimum modularity improvement to continue",
120✔
94
            min: 1e-10,
120✔
95
            max: 0.01,
120✔
96
            advanced: true,
120✔
97
        },
120✔
98
        useOptimized: {
120✔
99
            type: "boolean",
120✔
100
            default: true,
120✔
101
            label: "Use Optimized",
120✔
102
            description: "Use optimized implementation for better performance on large graphs",
120✔
103
            advanced: true,
120✔
104
        },
120✔
105
    };
120✔
106

107
    static suggestedStyles = (): SuggestedStylesConfig => ({
120✔
108
        layers: [
13✔
109
            {
13✔
110
                node: {
13✔
111
                    selector: "",
13✔
112
                    style: {
13✔
113
                        enabled: true,
13✔
114
                    },
13✔
115
                    calculatedStyle: {
13✔
116
                        inputs: ["algorithmResults.graphty.louvain.communityId"],
13✔
117
                        output: "style.texture.color",
13✔
118
                        expr: "{ return StyleHelpers.color.categorical.okabeIto(arguments[0] ?? 0) }",
13✔
119
                    },
13✔
120
                },
13✔
121
                metadata: {
13✔
122
                    name: "Louvain - Okabe-Ito Colors",
13✔
123
                    description: "8 vivid colorblind-safe community colors",
13✔
124
                },
13✔
125
            },
13✔
126
        ],
13✔
127
        description: "Visualizes graph communities through distinct colors",
13✔
128
        category: "grouping",
13✔
129
    });
13✔
130

131
    /**
132
     * Executes the Louvain algorithm on the graph
133
     *
134
     * Detects communities by optimizing modularity in a hierarchical manner.
135
     */
136
    async run(): Promise<void> {
19✔
137
        const g = this.graph;
7✔
138
        const nodes = Array.from(g.getDataManager().nodes.keys());
7✔
139

140
        if (nodes.length === 0) {
7!
141
            return;
×
142
        }
×
143

144
        // Get options from schema
145
        const {resolution, maxIterations, tolerance, useOptimized} = this.schemaOptions;
7✔
146

147
        // Convert to @graphty/algorithms format (truly undirected for community detection)
148
        // addReverseEdges: false creates an undirected graph required by louvain
149
        const graphData = toAlgorithmGraph(g, {addReverseEdges: false});
7✔
150

151
        // Run Louvain algorithm
152
        const result = louvain(graphData, {
7✔
153
            resolution,
7✔
154
            maxIterations,
7✔
155
            tolerance,
7✔
156
            useOptimized,
7✔
157
        });
7✔
158

159
        // Store community assignments for each node
160
        const communityMap = new Map<number | string, number>();
7✔
161
        for (let i = 0; i < result.communities.length; i++) {
7✔
162
            for (const nodeId of result.communities[i]) {
67✔
163
                communityMap.set(nodeId, i);
123✔
164
            }
123✔
165
        }
67✔
166

167
        // Store results on nodes
168
        for (const nodeId of nodes) {
7✔
169
            const communityId = communityMap.get(nodeId) ?? 0;
123!
170
            this.addNodeResult(nodeId, "communityId", communityId);
123✔
171
        }
123✔
172
    }
7✔
173
}
19✔
174

175
// Auto-register this algorithm when the module is imported
176
Algorithm.register(LouvainAlgorithm);
15✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc