Skip to content

Commit 4c59305

Browse files
committed
feat: add Waxman graph generation support with parameter validation
1 parent 1e8d0a3 commit 4c59305

2 files changed

Lines changed: 131 additions & 0 deletions

File tree

v2/graph/standard/common.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const (
2121
WattsStrogatz GraphType = "watts_strogatz"
2222
RandomGeometric GraphType = "random_geometric"
2323
RandomRegular GraphType = "random_regular"
24+
Waxman GraphType = "waxman"
2425
None GraphType = "none"
2526
)
2627

@@ -136,6 +137,20 @@ func StandardGraph(seed int, directed bool, weightFunc WeightedFunc, config Grap
136137
return nil, fmt.Errorf("invalid parameter 'k' for Random Regular graph")
137138
}
138139
return RandomRegularGraph(seed, directed, weightFunc, n, k)
140+
case Waxman:
141+
n, ok := config.Params["n"].(int)
142+
if !ok {
143+
return nil, fmt.Errorf("invalid parameter 'n' for Waxman graph")
144+
}
145+
alpha, ok := config.Params["alpha"].(float64)
146+
if !ok {
147+
return nil, fmt.Errorf("invalid parameter 'alpha' for Waxman graph")
148+
}
149+
beta, ok := config.Params["beta"].(float64)
150+
if !ok {
151+
return nil, fmt.Errorf("invalid parameter 'beta' for Waxman graph")
152+
}
153+
return WaxmanGraph(seed, directed, weightFunc, n, alpha, beta)
139154
case None:
140155
return graph.New(directed, true), nil
141156
default:

v2/graph/standard/waxman.go

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
package standard
2+
3+
import (
4+
"fmt"
5+
"math"
6+
7+
"github.com/elecbug/netkit/v2/graph"
8+
)
9+
10+
type waxmanPoint struct {
11+
x float64
12+
y float64
13+
}
14+
15+
func waxmanDistance(a, b waxmanPoint) float64 {
16+
dx := a.x - b.x
17+
dy := a.y - b.y
18+
return math.Sqrt(dx*dx + dy*dy)
19+
}
20+
21+
// WaxmanGraph generates a Waxman random graph.
22+
// Nodes are placed uniformly at random in a 2D unit square.
23+
// For each node pair (u, v), an edge is added with probability:
24+
//
25+
// P(u, v) = alpha * exp(-dist(u, v) / (beta * L))
26+
//
27+
// L is the maximum Euclidean distance among all node pairs.
28+
// alpha controls edge density.
29+
// beta controls locality sensitivity.
30+
func WaxmanGraph(seed int, directed bool, weightFunc WeightedFunc, n int, alpha, beta float64) (*graph.Graph, error) {
31+
if n < 0 {
32+
return nil, fmt.Errorf("invalid number of nodes: n must be non-negative")
33+
}
34+
if alpha <= 0 || alpha > 1 {
35+
return nil, fmt.Errorf("invalid alpha: alpha must be in (0, 1]")
36+
}
37+
if beta <= 0 || beta > 1 {
38+
return nil, fmt.Errorf("invalid beta: beta must be in (0, 1]")
39+
}
40+
41+
r := generateRand(seed)
42+
g := graph.New(directed, weightFunc != nil)
43+
44+
if weightFunc == nil {
45+
weightFunc = func(from, to *graph.Node) *graph.Weight {
46+
return nil
47+
}
48+
}
49+
50+
points := make([]waxmanPoint, n)
51+
52+
// --- 1. Generate Nodes and Positions ---
53+
for i := 0; i < n; i++ {
54+
id := graph.NodeID(fmt.Sprintf("%d", i))
55+
56+
if err := g.AddNode(id); err != nil {
57+
return nil, fmt.Errorf("failed to add node: %w", err)
58+
}
59+
60+
points[i] = waxmanPoint{
61+
x: r.Float64(),
62+
y: r.Float64(),
63+
}
64+
}
65+
66+
if n <= 1 {
67+
return g, nil
68+
}
69+
70+
// --- 2. Compute L: maximum pairwise distance ---
71+
L := 0.0
72+
73+
for i := 0; i < n; i++ {
74+
for j := i + 1; j < n; j++ {
75+
d := waxmanDistance(points[i], points[j])
76+
if d > L {
77+
L = d
78+
}
79+
}
80+
}
81+
82+
if L == 0 {
83+
return g, nil
84+
}
85+
86+
// --- 3. Generate Edges ---
87+
for i := 0; i < n; i++ {
88+
for j := i + 1; j < n; j++ {
89+
d := waxmanDistance(points[i], points[j])
90+
p := alpha * math.Exp(-d/(beta*L))
91+
92+
if r.Float64() >= p {
93+
continue
94+
}
95+
96+
from := graph.NodeID(fmt.Sprintf("%d", i))
97+
to := graph.NodeID(fmt.Sprintf("%d", j))
98+
99+
fromNode, err := g.Node(from)
100+
if err != nil {
101+
return nil, fmt.Errorf("failed to get node: %w", err)
102+
}
103+
104+
toNode, err := g.Node(to)
105+
if err != nil {
106+
return nil, fmt.Errorf("failed to get node: %w", err)
107+
}
108+
109+
if err := g.AddEdge(from, to, weightFunc(fromNode, toNode)); err != nil {
110+
return nil, fmt.Errorf("failed to add edge: %w", err)
111+
}
112+
}
113+
}
114+
115+
return g, nil
116+
}

0 commit comments

Comments
 (0)