48 double tol = 1e-9,
double full_fallback_fraction = 0.60) {
50 last_repaired_vertices_ = 0;
51 last_repair_iterations_ = 0;
52 last_residual_l1_ = 0.0;
53 last_residual_linf_ = 0.0;
57 const auto previous_n = rank_.size();
58 const bool had_dangling_before = dangling_vertices_ != 0;
63 std::vector<VertexId> dangling_candidates;
65 for (
const auto& e : batch.
updates) {
66 dangling_candidates.push_back(e.src);
67 if (!
is_directed(g_) && e.dst != e.src) dangling_candidates.push_back(e.dst);
69 std::sort(dangling_candidates.begin(), dangling_candidates.end());
70 dangling_candidates.erase(
71 std::unique(dangling_candidates.begin(), dangling_candidates.end()),
72 dangling_candidates.end());
78 dangling_vertices_ = 0;
79 last_repaired_vertices_ = 0;
80 last_repair_iterations_ = 0;
81 last_residual_l1_ = 0.0;
82 last_residual_linf_ = 0.0;
83 last_full_recompute_converged_ =
true;
91 if (n != previous_n || had_dangling_before) {
95 for (
const auto v : dangling_candidates) {
101 dangling_vertices_ = 0;
103 std::vector<std::uint8_t> active(n, 0);
104 std::size_t active_count = 0;
106 if (v < n && !active[v]) {
112 for (
const auto& e : batch.
updates) {
119 if (active_count == 0) {
120 last_repaired_vertices_ = 0;
121 last_repair_iterations_ = 0;
122 last_residual_l1_ = 0.0;
123 last_residual_linf_ = 0.0;
127 const auto fallback_limit =
static_cast<std::size_t
>(
128 std::max(1.0, full_fallback_fraction *
static_cast<double>(n)));
129 if (active_count >= fallback_limit) {
134 const double base = (1.0 - damping_) /
static_cast<double>(n);
135 std::vector<std::uint8_t> ever_active = active;
136 last_repair_iterations_ = 0;
137 last_residual_l1_ = 0.0;
138 last_residual_linf_ = 0.0;
139 last_full_recompute_converged_ =
false;
141 for (std::size_t it = 0; it < local_iterations && active_count != 0; ++it) {
142 ++last_repair_iterations_;
143 auto next_rank = rank_;
144 std::vector<std::uint8_t> next_active(n, 0);
145 std::size_t next_count = 0;
146 double iter_l1 = 0.0;
147 double iter_linf = 0.0;
149 auto activate_next = [&](
VertexId v) {
150 if (v < n && !next_active[v]) {
158 if (!active[v])
continue;
160 double incoming = 0.0;
163 if (out_degree != 0) incoming += rank_[u] /
static_cast<double>(out_degree);
166 const double updated = base + damping_ * incoming;
167 const double delta = std::abs(updated - rank_[v]);
168 next_rank[v] = updated;
170 iter_linf = std::max(iter_linf, delta);
175 rank_.swap(next_rank);
176 active.swap(next_active);
177 active_count = next_count;
178 last_residual_l1_ = iter_l1;
179 last_residual_linf_ = iter_linf;
181 std::size_t repaired = 0;
182 for (
auto flag : ever_active) repaired += flag != 0;
183 if (repaired >= fallback_limit) {
189 last_repaired_vertices_ = 0;
190 for (
auto flag : ever_active) last_repaired_vertices_ += flag != 0;
193 void recompute(std::size_t max_iterations = 200,
double tol = 1e-12) {
194 const auto result = full_solve(max_iterations, tol);
195 rank_ = result.values;
196 dangling_vertices_ = count_dangling_vertices();
198 last_repair_iterations_ = result.iterations;
199 last_residual_l1_ = result.residual_l1;
200 last_residual_linf_ = result.residual_linf;
201 last_full_recompute_converged_ = result.converged;
205 std::size_t reference_max_iterations = 500,
206 double reference_tol = 1e-12,
207 double l1_tolerance = 1e-6,
208 double linf_tolerance = 1e-7)
const {
209 const auto reference = full_solve(reference_max_iterations, reference_tol);
218 const auto count = std::min(rank_.size(), reference.values.size());
219 for (std::size_t i = 0; i < count; ++i) {
220 const double error = std::abs(rank_[i] - reference.values[i]);
224 if (rank_.size() != reference.values.size()) {
225 validation.
l1_error = std::numeric_limits<double>::infinity();
226 validation.
linf_error = std::numeric_limits<double>::infinity();
229 validation.
l1_error <= l1_tolerance &&
236 std::size_t local_iterations = 64,
237 double local_tol = 1e-10,
238 double full_fallback_fraction = 0.95,
239 std::size_t reference_max_iterations = 500,
240 double reference_tol = 1e-12,
241 double l1_tolerance = 1e-6,
242 double linf_tolerance = 1e-7) {
243 apply(batch, local_iterations, local_tol, full_fallback_fraction);
245 l1_tolerance, linf_tolerance);
246 if (!validation.within_tolerance) {
247 const auto reference = full_solve(reference_max_iterations, reference_tol);
248 rank_ = reference.values;
249 dangling_vertices_ = count_dangling_vertices();
251 last_repair_iterations_ = reference.iterations;
252 last_residual_l1_ = reference.residual_l1;
253 last_residual_linf_ = reference.residual_linf;
254 last_full_recompute_converged_ = reference.converged;
255 validation.fallback_applied =
true;