hyperbolic_graph_generator/
hg_math.rs1
2use std::process;
29use std::f64;
30
31use crate::hg_debug::*;
32use crate::hg_formats::*;
33
34
35fn rho(alpha: f64, rr: f64, r: f64) -> f64 {
36 return alpha * (alpha * (r - rr)).exp();
37}
38
39fn hg_heaviside(zeta: f64, rr : f64, r1: f64, r2: f64, theta: f64) -> f64 {
40 let x = (1.0 / zeta)
41 * ((zeta * r1).cosh() * (zeta * r2).cosh()
42 - (zeta * r1).sinh() * (zeta * r2).sinh() * theta.cos()).acosh();
43 if x >= rr {
44 return 0.0;
45 } else {
46 return 1.0;
47 }
48}
49
50fn hg_fermi_dirac_std(beta: f64, zeta: f64, rr: f64, r1: f64, r2: f64, theta: f64) -> f64 {
51 let x = (1.0 / zeta)
52 * ((zeta * r1).cosh() * (zeta * r2).cosh()
53 -(zeta * r1).sinh() * (zeta * r2).sinh() * theta.cos()).acosh();
54
55 return 1.0 / (1.0 + (beta * zeta / 2.0 * (x - rr)).exp());
56}
57
58fn hg_fermi_dirac_scm(beta: f64, rr: f64, r1: f64, r2: f64) -> f64 {
59 return 1.0 / (1.0 + ((beta / 2.0) * (r1 + r2 - rr)).exp());
60}
61
62fn hg_integral_heaviside(x: &[f64], _dim: usize, fp: &HgFParams) -> f64 {
63 return 1.0 / HG_PI
64 * rho(fp.alpha, fp.rr, x[0])
65 * rho(fp.alpha, fp.rr, x[1])
66 * hg_heaviside(fp.zeta, fp.rr, x[0], x[1], x[2]);
67}
68
69fn hg_integral_standard(x: &[f64], _dim: usize, fp: &HgFParams) -> f64 {
70 return 1.0 / HG_PI
71 * rho(fp.alpha, fp.rr, x[0])
72 * rho(fp.alpha, fp.rr, x[1])
73 * hg_fermi_dirac_std(fp.beta, fp.zeta, fp.rr, x[0], x[1], x[2]);
74}
75
76fn hg_integral_scm(x: &[f64], _dim: usize, fp: &HgFParams) -> f64 {
77 return rho(fp.alpha, fp.rr, x[0])
78 * rho(fp.alpha, fp.rr, x[1])
79 * hg_fermi_dirac_scm(fp.eta, fp.rr, x[0], x[1]);
80}
81
82pub fn hg_get_rr(pg: &HgParametersType, p: &HgAlgorithmParametersType) -> f64 {
83 let calls = 100000; let mut xl = [0.0f64; 3];
85 let mut xu = [0.0f64; 3];
86
87 let (dim, mut params) = match pg.gtype {
90 HgGraphType::SoftConfigurationModel => {
91 (2, HgFParams::new(0.0, p.alpha, -1.0, p.eta, -1.0))
92 },
93 HgGraphType::HyperbolicStandard => {
94 xu[2] = HG_PI;
95 (3, HgFParams::new(0.0, p.alpha, pg.zeta_eta, -1.0, 1.0 / pg.temperature))
96 },
97 _ => {
98 xu[2] = HG_PI;
99 (3, HgFParams::new(0.0, p.alpha, pg.zeta_eta, -1.0, -1.0))
100 }
101 };
102
103 let cb = match pg.gtype {
104 HgGraphType::SoftConfigurationModel => hg_integral_scm,
105 HgGraphType::HyperbolicStandard => hg_integral_standard,
106 _ => hg_integral_heaviside
107 };
108
109 rgsl::RngType::env_setup();
110 let t : rgsl::RngType = rgsl::rng::default();
111 let mut r = rgsl::Rng::new(&t).unwrap();
112 let mut s = rgsl::MiserMonteCarlo::new(dim).unwrap();
113
114 let n = pg.expected_n as f64;
115 let k_bar = pg.expected_degree as f64;
116
117 let eps = 0.01f64; let mut low = 0f64;
119 let e = if params.beta < 1.0 { 2.5 } else { 2.0 };
120 let mut high = n.ln().powf(e).max(50.0);
121 let mut res = 0.0;
122 let mut mid = 0.0;
123
124 for _ in 0..5000 {
125 mid = (high + low) / 2.0;
127 xu[0] = mid;
128 xu[1] = mid;
129 params.rr = mid; let (_res, _err) = s.integrate(dim, |k| { cb(k, dim, ¶ms) }, &mut xl, &mut xu, calls, &mut r).unwrap();
133 res = _res;
134
135 if res.is_nan() {
136 mid *= 1.00001;
137 } else {
138 if (n * res) < k_bar {
140 high = mid;
141 } else {
142 low = mid;
143 }
144 }
145
146 hg_debug!("{} - {} - {}", it, n * res, params.rr);
147 if ((n * res - k_bar).abs() <= eps && !res.is_nan()) || (high <= f64::MIN_POSITIVE) {
148 break;
149 }
150 }
151
152 if res.is_nan() || ((n * res - k_bar).abs() > eps) || (high < f64::MIN_POSITIVE) {
153 eprintln!("Network cannot be generated. Try different parameters.");
154 process::exit(1);
155 }
156
157 return mid;
158}
159
160fn hypergeometric_f(_a: f64, mut b: f64, _c: f64, z: f64) -> f64 {
162 if b == 1.0 {
163 return -1.0 * (1.0 - z).ln() / z;
164 } else {
165 let w = 1.0 / (1.0 - z);
166
167 if ((b as i64) as f64) == b {
170 let eps = 0.000001f64;
171 b += eps;
172 }
173
174 let f = w * b / (b - 1.0) * rgsl::hypergeometric::hyperg_2F1(1.0, 1.0, 2.0 - b, w)
175 + b * HG_PI * (1.0 - w).powf(-b) * w.powf(b) * (b * HG_PI).sin().powf(-1.0);
176
177 return f;
178 }
179}
180
181pub fn hg_get_lambda(params: &HgParametersType, _p: &HgAlgorithmParametersType) -> f64 {
182 let beta = 1.0 / params.temperature;
183 let n = params.expected_n as f64;
184 let k_bar = params.expected_degree as f64;
185
186 let eps = 0.001; let mut low = 1.0;
188 let mut high = rgsl::DBL_MAX;
189 let mut mid;
190
191 loop {
192 mid = (high + low) / 2.0;
194
195 let res = hypergeometric_f(1.0, 1.0 / beta, 1.0 + 1.0 / beta, -mid);
196
197 if (n * res) < k_bar {
198 high = mid;
199 } else {
200 low = mid;
201 }
202
203 if (n * res - k_bar).abs() <= eps && !res.is_nan() {
205 break;
206 }
207 }
208 return mid;
209}