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

graphty-org / graphty-element / 20390753610

20 Dec 2025 06:53AM UTC coverage: 82.423% (-1.2%) from 83.666%
20390753610

push

github

apowers313
Merge branch 'master' of https://github.com/graphty-org/graphty-element

5162 of 6088 branches covered (84.79%)

Branch coverage included in aggregate %.

24775 of 30233 relevant lines covered (81.95%)

6480.4 hits per line

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

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

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

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

65
    static zodOptionsSchema: ZodOptionsSchema = louvainOptionsSchema;
108✔
66

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

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

128
    // eslint-disable-next-line @typescript-eslint/require-await
129
    async run(): Promise<void> {
7✔
130
        const g = this.graph;
7✔
131
        const nodes = Array.from(g.getDataManager().nodes.keys());
7✔
132

133
        if (nodes.length === 0) {
7!
134
            return;
×
135
        }
×
136

137
        // Get options from schema
138
        const {resolution, maxIterations, tolerance, useOptimized} = this.schemaOptions;
7✔
139

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

144
        // Run Louvain algorithm
145
        const result = louvain(graphData, {
7✔
146
            resolution,
7✔
147
            maxIterations,
7✔
148
            tolerance,
7✔
149
            useOptimized,
7✔
150
        });
7✔
151

152
        // Store community assignments for each node
153
        const communityMap = new Map<number | string, number>();
7✔
154
        for (let i = 0; i < result.communities.length; i++) {
7✔
155
            for (const nodeId of result.communities[i]) {
67✔
156
                communityMap.set(nodeId, i);
123✔
157
            }
123✔
158
        }
67✔
159

160
        // Store results on nodes
161
        for (const nodeId of nodes) {
7✔
162
            const communityId = communityMap.get(nodeId) ?? 0;
123!
163
            this.addNodeResult(nodeId, "communityId", communityId);
123✔
164
        }
123✔
165
    }
7✔
166
}
7✔
167

168
// Auto-register this algorithm when the module is imported
169
Algorithm.register(LouvainAlgorithm);
3✔
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

© 2026 Coveralls, Inc