Source code for qbiocode.apps.quvine.walks.ctqw
# Copyright 2026, IBM Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import networkx as nx
import numpy as np
from qbiocode.apps.quvine._deps import require_module
from qbiocode.apps.quvine.utils.utilities import sample_walks_from_distribution
def _hiperwalk():
"""Resolve hiperwalk at call time, not import time.
hiperwalk is provided by the [quvine] extra; require_module turns its absence
into a message naming the extra and the install command rather than a bare
ModuleNotFoundError. Resolving it at *import* time would be wrong: walks/base.py
imports this module eagerly, so an RWR-only run -- which never touches a quantum
walk -- would fail with a CTQW message pointing at the wrong dependency.
"""
return require_module("hiperwalk", feature="continuous-time quantum walks (CTQW)")
[docs]
def generate_ctqw_hiperwalk_scores(G, root, view_nodes=None, steps: int=20, time: float | None=None, gamma: float | None=None):
"""
Return node probabilities from Hiperwalk continuous-time quantum walk (CTQW)
Args:
G (_type_): _description_
root (_type_): _description_
view_nodes (_type_, optional): _description_. Defaults to None.
steps (int, optional): _description_. Defaults to 20.
time (float | None, optional): _description_. Defaults to None.
gamma (float | None, optional): _description_. Defaults to None.
"""
if view_nodes is not None:
G = G.subgraph(view_nodes)
if root not in G:
raise ValueError("Root node not in graph or view")
nodes = list(G.nodes())
node2i = {n:i for i,n in enumerate(nodes)}
i2node = {i:n for n,i in node2i.items()}
G_int = nx.relabel_nodes(G,node2i, copy=True)
#build hiperwalk graph + ctqw
hpw = _hiperwalk()
hg = hpw.Graph(G_int)
qw = hpw.ContinuousTime(graph=hg)
if gamma is not None:
qw.set_gamma(gamma)
if time is not None:
qw.set_time(time)
root_i = node2i[root]
state0 = qw.ket(root_i)
#simulate to obtain final state at steps
final_state = qw.simulate(range=(steps, steps+1), state=state0)
#convert to node probability
probs = qw.probability_distribution(final_state)
probs = np.asarray(probs)[0]
#map to original ids
scores = {i2node[i]: float(probs[i]) for i in range(len(probs))}
return scores
[docs]
def generate_CTQW_walks(G, root, view_nodes=None, num_walks: int = 10,
walk_length: int = 6, steps: int=20,
time: float | None=None,
gamma: float | None=None,
rng=None):
assert isinstance(rng, np.random.Generator)
scores = generate_ctqw_hiperwalk_scores(G,
root=root,
view_nodes=view_nodes,
steps=steps,
time=time,
gamma=gamma)
walks = sample_walks_from_distribution(scores,
num_walks=num_walks,
walk_length=walk_length,
rng=rng)
return walks