Skip to content

Commit 398d693

Browse files
authored
Merge pull request #43 from elecbug/elecbug/master
Update v0.12.3
2 parents 7d8f9cd + 44b727c commit 398d693

28 files changed

Lines changed: 3832 additions & 113 deletions

v2/graph/analyzer/analyzer.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,17 @@ type Analyzer struct {
1313
allShortestPaths map[graph.NodeID]map[graph.NodeID][]graph.Path // allShortestPaths caches the results of shortest path computations between node pairs.
1414
mu sync.RWMutex // mu protects access to the allShortestPaths cache to ensure thread safety during concurrent reads/writes.
1515
parallelCoreCount int // parallelCoreCount determines how many CPU cores to utilize for parallel computations, if applicable.
16+
cfg *Config // cfg holds configuration options for the analyzer, such as worker counts and normalization settings.
1617
}
1718

1819
// NewAnalyzer creates a new Analyzer instance based on the provided graph.
19-
func NewAnalyzer(g *graph.Graph, parallelCoreCount int) *Analyzer {
20+
func NewAnalyzer(g *graph.Graph, parallelCoreCount int, cfg *Config) *Analyzer {
2021
return &Analyzer{
2122
baseGraph: g,
2223
graphHash: "",
2324
allShortestPaths: make(map[graph.NodeID]map[graph.NodeID][]graph.Path),
2425
parallelCoreCount: parallelCoreCount,
26+
cfg: cfg,
2527
}
2628
}
2729

v2/graph/analyzer/analyzer_test.go

Lines changed: 146 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package analyzer_test
22

33
import (
4+
"encoding/json"
45
"fmt"
6+
"io/fs"
57
"math/rand"
8+
"os"
69
"testing"
710
"time"
811

@@ -16,7 +19,6 @@ import (
1619
func TestShortestPaths(t *testing.T) {
1720
fmt.Println("Test Shortest Paths")
1821
testComputeShortestPath(t)
19-
testPerformance(t)
2022
}
2123

2224
// testComputeShortestPath sets up a simple graph and tests the ComputeAllShortestPaths and ShortestPaths
@@ -34,7 +36,7 @@ func testComputeShortestPath(t *testing.T) {
3436
g.AddEdge("A", "C", graph.NewWeight(2))
3537
g.AddEdge("C", "D", graph.NewWeight(1))
3638

37-
a := analyzer.NewAnalyzer(g, 1)
39+
a := analyzer.NewAnalyzer(g, 1, analyzer.DefaultConfig())
3840

3941
paths, err := a.ShortestPaths("A", "D")
4042
if err != nil {
@@ -87,24 +89,24 @@ func testComputeShortestPath(t *testing.T) {
8789
}
8890
}
8991

90-
// testPerformance creates a larger random graph and tests the performance of the ShortestPaths method with different
92+
// TestPerformance creates a larger random graph and tests the performance of the ShortestPaths method with different
9193
// parallel core counts. It measures the time taken to compute shortest paths and to retrieve cached results, ensuring
9294
// that the method works correctly and efficiently under various conditions.
93-
func testPerformance(t *testing.T) {
94-
fmt.Println("- Test Performance")
95+
func TestPerformance(t *testing.T) {
96+
fmt.Println("Test Performance")
9597

9698
g, err := standard.ErdosRenyiGraph(
9799
42,
98100
false,
99-
func(from, to graph.NodeID) *graph.Weight { return graph.NewWeight(rand.Float64() * 100) },
101+
func(from, to *graph.Node) *graph.Weight { return graph.NewWeight(rand.Float64() * 100) },
100102
1000,
101103
0.01,
102104
)
103105
if err != nil {
104106
t.Fatalf("failed to create graph: %v", err)
105107
}
106108

107-
a := analyzer.NewAnalyzer(g, 1)
109+
a := analyzer.NewAnalyzer(g, 1, analyzer.DefaultConfig())
108110

109111
startTime := time.Now()
110112
paths, err := a.ShortestPaths("0", "999")
@@ -119,7 +121,7 @@ func testPerformance(t *testing.T) {
119121
duration := time.Since(startTime)
120122
fmt.Printf(" - Time taken to compute shortest paths: %v\n", duration)
121123

122-
a = analyzer.NewAnalyzer(g, 4)
124+
a = analyzer.NewAnalyzer(g, 4, analyzer.DefaultConfig())
123125

124126
startTime = time.Now()
125127
pathsCompared, err := a.ShortestPaths("0", "999")
@@ -133,7 +135,7 @@ func testPerformance(t *testing.T) {
133135
t.Errorf("expected total distance %v, got %v", paths[0].TotalDistance(), pathsCompared[0].TotalDistance())
134136
}
135137

136-
a = analyzer.NewAnalyzer(g, 16)
138+
a = analyzer.NewAnalyzer(g, 16, analyzer.DefaultConfig())
137139

138140
startTime = time.Now()
139141
pathsCompared, err = a.ShortestPaths("0", "999")
@@ -147,7 +149,7 @@ func testPerformance(t *testing.T) {
147149
t.Errorf("expected total distance %v, got %v", paths[0].TotalDistance(), pathsCompared[0].TotalDistance())
148150
}
149151

150-
a = analyzer.NewAnalyzer(g, 32)
152+
a = analyzer.NewAnalyzer(g, 32, analyzer.DefaultConfig())
151153

152154
startTime = time.Now()
153155
pathsCompared, err = a.ShortestPaths("0", "999")
@@ -191,3 +193,137 @@ func equalPathSlices(a, b graph.Path) bool {
191193

192194
return true
193195
}
196+
197+
func TestAnaylzer(t *testing.T) {
198+
fmt.Println("Test Analyzer")
199+
200+
results := make(map[string]interface{})
201+
g, err := standard.ErdosRenyiGraph(time.Now().Nanosecond(), false, nil, 1000, 0.01)
202+
if err != nil {
203+
t.Fatalf("Failed to create graph: %v", err)
204+
}
205+
206+
cfg := analyzer.DefaultConfig()
207+
a := analyzer.NewAnalyzer(g, 16, cfg)
208+
209+
t.Run("ShortestPaths", func(t *testing.T) {
210+
results["shortest_paths"] = make(map[string]any)
211+
212+
for i := 0; i < 10; i++ {
213+
for j := 0; j < 10; j++ {
214+
if i == j {
215+
continue
216+
}
217+
218+
paths, err := a.ShortestPaths(graph.NodeID(fmt.Sprintf("%d", i*100)), graph.NodeID(fmt.Sprintf("%d", j*100)))
219+
if err != nil {
220+
t.Fatalf("Failed to compute shortest paths: %v", err)
221+
}
222+
223+
results["shortest_paths"].(map[string]any)[fmt.Sprintf("%d->%d", i*100, j*100)] = paths
224+
}
225+
}
226+
})
227+
t.Run("BetweennessCentrality", func(t *testing.T) {
228+
res, err := a.BetweennessCentrality()
229+
if err != nil {
230+
t.Fatalf("Failed to compute betweenness centrality: %v", err)
231+
}
232+
233+
results["betweenness_centrality"] = res
234+
})
235+
t.Run("ClosenessCentrality", func(t *testing.T) {
236+
res, err := a.ClosenessCentrality()
237+
if err != nil {
238+
t.Fatalf("Failed to compute closeness centrality: %v", err)
239+
}
240+
241+
results["closeness_centrality"] = res
242+
})
243+
t.Run("ClusteringCoefficient", func(t *testing.T) {
244+
gcc, ccs, err := a.ClusteringCoefficient()
245+
if err != nil {
246+
t.Fatalf("Failed to compute clustering coefficient: %v", err)
247+
}
248+
249+
results["clustering_coefficient"] = map[string]any{
250+
"global": gcc,
251+
"local": ccs,
252+
"average": func() float64 {
253+
sum := 0.0
254+
for _, v := range ccs {
255+
sum += v
256+
}
257+
return sum / float64(len(ccs))
258+
}(),
259+
}
260+
})
261+
t.Run("DegreeAssortativityCoefficient", func(t *testing.T) {
262+
res, err := a.DegreeAssortativityCoefficient()
263+
if err != nil {
264+
t.Fatalf("Failed to compute degree assortativity coefficient: %v", err)
265+
}
266+
267+
results["degree_assortativity_coefficient"] = res
268+
})
269+
t.Run("DegreeCentrality", func(t *testing.T) {
270+
res, err := a.DegreeCentrality()
271+
if err != nil {
272+
t.Fatalf("Failed to compute degree centrality: %v", err)
273+
}
274+
275+
results["degree_centrality"] = res
276+
})
277+
t.Run("Diameter", func(t *testing.T) {
278+
res, weight, err := a.Diameter()
279+
if err != nil {
280+
t.Fatalf("Failed to compute diameter: %v", err)
281+
}
282+
283+
results["diameter"] = res
284+
results["diameter_weight"] = weight
285+
})
286+
t.Run("EdgeBetweennessCentrality", func(t *testing.T) {
287+
res, err := a.EdgeBetweennessCentrality()
288+
if err != nil {
289+
t.Fatalf("Failed to compute edge betweenness centrality: %v", err)
290+
}
291+
results["edge_betweenness_centrality"] = res
292+
})
293+
t.Run("EigenvectorCentrality", func(t *testing.T) {
294+
res, err := a.EigenvectorCentrality()
295+
if err != nil {
296+
t.Fatalf("Failed to compute eigenvector centrality: %v", err)
297+
}
298+
results["eigenvector_centrality"] = res
299+
})
300+
t.Run("Modularity", func(t *testing.T) {
301+
res, err := a.Modularity()
302+
if err != nil {
303+
t.Fatalf("Failed to compute modularity: %v", err)
304+
}
305+
results["modularity"] = res
306+
})
307+
t.Run("PageRank", func(t *testing.T) {
308+
res, err := a.PageRank()
309+
if err != nil {
310+
t.Fatalf("Failed to compute page rank: %v", err)
311+
}
312+
results["page_rank"] = res
313+
})
314+
315+
jsonResults, err := json.MarshalIndent(results, "", " ")
316+
317+
if err != nil {
318+
t.Fatalf("Failed to marshal results: %v", err)
319+
}
320+
321+
os.WriteFile("metrics.log", jsonResults, fs.ModePerm)
322+
323+
gJson, err := g.Serialize()
324+
if err != nil {
325+
t.Fatalf("Failed to serialize graph: %v", err)
326+
}
327+
328+
os.WriteFile("graph.log", []byte(gJson), fs.ModePerm)
329+
}

0 commit comments

Comments
 (0)