Expand description
Recent-degree preferential attachment with vertex aging (ALGO-GN-032).
Counterpart of igraph_recent_degree_aging_game() in
references/igraph/src/games/recent_degree.c (lines 228-381).
Each step i ≥ 1 adds a fresh vertex and attaches m
(or outseq[i]) outgoing edges. Targets are drawn from a Fenwick BIT
(PsumTree) weighted by a product of a recent-degree term and an
age term:
weight(v) = (pow(recent_deg(v), pa_exp) + zero_appeal)
* pow(age(v), aging_exp)where recent_deg(v) counts edges received from v in the last
time_window steps (sliding window), and
age(v) = (i − v) / binwidth + 1 with
binwidth = nodes / aging_bins + 1.
§Hybrid of GN-019 and GN-021
This model combines:
- GN-019
recent_degree_game— the sliding-window FIFO over(vertex, step)pairs that decrements counters on expiry and a per-step sentinel separator. - GN-021
barabasi_aging_game— the binwidth-based age computation and the periodic age-sweep (everyk · binwidth ≤ i).
The C source uses a slightly simpler weight formula than the
barabasi-aging variant: only one zero term (zero_appeal) added
to the degree component, no separate zero_age_appeal, deg_coef,
or age_coef. The age term is pow(age, aging_exp) directly with no
shift in coefficient.
§Mechanics (mirrors C lines 305-369)
For each step i from 1 to nodes − 1:
- Window expiry (only when
i ≥ time_window): pop entries from the front ofhistoryuntil a sentinelNoneis popped. For each popped vertexj, decrementdegree[j]and refreshpsumtree[j]with the NEW degree but the age computed at the moment of expiry — matching the C source’s(pow(deg, pa_exp) + zero_appeal) * pow((i - j)/binwidth + 1, aging_exp). - Draw
m(oroutseq[i]) targets, each viapsumtree.search_bounded(uniform(0, sum), i)— with a uniform fallback over[0, i)whensum == 0. - For each chosen target: bump
degree[to], pushSome(to)tohistory, record edge(i, to). - Push sentinel
Nonetohistory. - Refresh psumtree entries for each chosen target with their new weight (recent-degree + age).
- Insert vertex
i: ifoutpref, bumpdegree[i] += no_of_neighborsand refreshpsumtree[i]; otherwise set it tozero_appeal. Note thatoutprefcontributions todegree[i]are permanent — they are NOT pushed to the history deque (matching C lines 349-356 which update degree+psumtree but not history). - Age sweep: for every
k ≥ 1withk · binwidth ≤ i, refresh vertexi − k · binwidthwith agek + 2.
§Parameters
nodes— vertex count.m— per-step out-degree whenoutseqisNone.outseq— optional length-nodesvector of per-step out-degrees. The first entry is ignored (vertex 0 has no preceding vertices to cite), matching the upstream contract.outpref— whentrue, the new vertex’s own emitted citations feed back into its recent-degree counter (so its self-weight in the BIT is non-zero immediately).pa_exp— preferential-attachment exponent on recent degree.aging_exp— aging exponent on age bin (usually negative — younger vertices are favoured).aging_bins— must be≥ 1. Setsbinwidth = nodes / aging_bins + 1.time_window— sliding-window length.0means the window is always expired, so every vertex’s recent degree decays to zero immediately on the next step — effectively a pure-aging model withzero_appealfloor.zero_appeal— additive degree term so zero-recent-degree vertices have non-zero weight (whenaging_expis non-negative). Must be non-negative.directed— emit directed edges (newer → older convention).seed— initialises an internalSplitMix64.
§Construction guarantees
- No self-loops by construction — the new vertex
iis added to the BIT after itsmoutgoing draws, andsearch_bounded(_, i)prevents binary-lifting from over-advancing to positioni. - Multi-edges allowed when
m ≥ 2— independent draws against the same snapshot of the BIT can collide. Matches upstream’s behaviour (no within-step zeroing). - Zero-sum fallback: when the BIT total is zero, the draw falls
back to a uniform sample over
[0, i). This fires when all current vertices have decayed to zero weight (rare, but possible with very smalltime_windowandzero_appeal = 0).
§Cost
O((|V| + |V|/aging_bins + |E|) · log |V|) per igraph C documentation:
BIT operations dominate, while sliding-window expiry is amortised
O(|E|) because each vertex is inserted into history exactly once
and popped at most once.
§Notes on the C special case
weight_from_degree here mirrors C’s pow(degree, pa_exp) + zero_appeal,
preserving pow(0, 0) == 1 (degree=0 with pa_exp=0 ⇒ degree term = 1).
Functions§
- recent_
degree_ aging_ game - Recent-degree preferential attachment with vertex aging.