import logging
from typing import Any, Union, List, Tuple, Literal
import numpy as np
import torch
from libam.algorithms.unrestricted.FUGAL.Fugal import Fugal as _Fugal
from libam.algorithms.unrestricted.GrampaS.GrampaS import GrampaS as _GrampaS
from libam.algorithms.unrestricted.Grampa.Grampa import Grampa as _Grampa
from libam.algorithms.unrestricted.GWL.gwl import GWL as _GWL
from libam.algorithms.unrestricted.GWL.sgwl import SGWL as _SGWL
from libam.algorithms.unrestricted.CONE.conealign import Cone as _Cone
from libam.algorithms.unrestricted.NSD.NSD import NSD as _NSD
from libam.algorithms.unrestricted.Path.path import Path as _Path
from libam.algorithms.unrestricted.MMNC.mmnc import Mmnc as _Mmnc
from libam.algorithms.unrestricted.DSPP.Dspp import DSPP as _DSPP
from libam.algorithms.unrestricted.GrASp.grasp import Grasp as _Grasp
from libam.algorithms.unrestricted.KLAUS.klaus import Klaus as _Klaus
from libam.algorithms.unrestricted.LREA.lrea import Lrea as _Lrea
from libam.algorithms.unrestricted.MDS.mds import Mds as _Mds
from libam.algorithms.unrestricted.NetAlign.netalign import NetAlign as _NetAlign
from libam.algorithms.unrestricted.REGAL.regal import Regal as _Regal
from libam.algorithms.unrestricted.PARROT.Parrot import Parrot as _Parrot
from libam.algorithms.unrestricted.isorank.isorank import Isorank as _Isorank
from libam.algorithms.unrestricted.Got.got import Got as _Got
from libam.algorithms.unrestricted.Fgot.fgot import Fgot as _FGot
from libam.algorithms.restricted.GradP.gradp import GradP as _GradP
from libam.algorithms.restricted.JOENA.joena import Joena as _Joena
from libam.algorithms.restricted.HTC.htc import HTC as _Htc
from libam.algorithms.restricted.SlotA.slot_a import SlotA as _SlotA
from libam.algorithms.restricted.ALPINE.alpine import Alpine as _ALPINE
#from libam.algorithms.NotImplemented.NextAlign.NextA import NextAlign as _NextAlign
from libam.graph.graph_pair import GraphPair as _GraphPair
from libam.algorithms.algorithm import AlignAlgorithm
logger = logging.getLogger(__name__)
def _warn_unused(name:str, **kwargs) -> None:
for key in kwargs:
logger.warning(f"{name} received unknown kwarg '{key}' - it will be ignored")
[docs]
def fugal(pair: _GraphPair, iterations: int = 1, simple: bool = True, mu: float = 0.05, efn: int = 3, **kwargs: Any) -> _Fugal:
"""
Feature-fortified Unrestricted Graph Alignment.
:param pair: Graph Pair object
:param iterations: Number of iterations for the alignment
:param simple: Whether to use simple feature extraction
:param mu: Regularisation parameter
:param efn: Edge feature number (default 5 = standard FUGAL)
"""
alg = _Fugal(pair, iterations=iterations, simple=simple, mu=mu, efn=efn)
_warn_unused(alg.name, **kwargs)
return alg
[docs]
def grampa_s(pair: _GraphPair, eta: float = 0.2, init_sim: int = 1, eig_type: int = 0, **kwargs: Any) -> _GrampaS:
"""
GRAMPA-S
:param pair: Graph Pair object
:param eta: Regularisation parameter
:param init_sim: Initial similarity type
:param eig_type: Eigenvalue type
"""
alg = _GrampaS(pair, eta=eta, initSim=init_sim, eig_type=eig_type)
_warn_unused(alg.name, **kwargs)
return alg
[docs]
def grampa(pair: _GraphPair, eta: float = 0.2, **kwargs: Any) -> _Grampa:
"""
GRAMPA
:param pair: Graph Pair object
:param eta: Regularisation parameter
"""
alg = _Grampa(pair, eta=eta)
_warn_unused(alg.name, **kwargs)
return alg
[docs]
def cone(
pair: _GraphPair,
dim: int = 128,
window: int = 10,
negative: float = 1.0,
niter_init: int = 10,
reg_init: float = 1.0,
lr: float = 1.0,
bsz: int = 10,
nepoch: int = 5,
niter_align: int = 10,
reg_align: float = 0.05,
embsim: str = "euclidean",
numtop: int = 10,
**kwargs: Any,
) -> _Cone:
"""
CONE (Consistent Network Alignment with Proximity-Preserving Node Embedding)
:param pair: Graph Pair object
:param dim: Embedding dimension
:param window: Context window size
:param negative:
:param niter_init:
:param reg_init:
:param lr: Learning rate
:param bsz: Batch size
:param nepoch: Number of epochs
:param niter_align:
:param reg_align:
:param embsim: Embedding similarity metric
:param numtop: Number of top candidates
"""
alg = _Cone(pair, dim=dim, window=window, negative=negative, niter_init=niter_init,
reg_init=reg_init, lr=lr, bsz=bsz, nepoch=nepoch, niter_align=niter_align,
reg_align=reg_align, embsim=embsim, numtop=numtop)
_warn_unused(alg.name, **kwargs)
return alg
[docs]
def gwl(
pair: _GraphPair,
# Optimiser options
epochs: int = 1,
batch_size: int = 64,
use_cuda: bool = False,
strategy: str = 'hard',
beta: float = 0.1,
outer_iteration: int = 20,
inner_iteration: int = 10,
sgd_iteration: int = 200,
display: bool = False,
prefix: str = '',
prior: bool = False,
# Hyperparameters
dimension: int = 64,
loss_type: str = 'L2',
cost_type: str = 'cosine',
ot_method: str = 'proximal',
# Scheduler / hardware
lr: float = 0.01,
gamma: float = 0.5,
max_cpu: int = 0,
**kwargs: Any,
) -> _GWL:
"""
GWL (Gromov-Wasserstein Learning)
:param pair: Graph Pair object
:param epochs: Training epochs
:param batch_size: Batch size
:param use_cuda: Use CUDA if available
:param strategy: Sampling strategy, 'hard' or 'soft'
:param beta:
:param outer_iteration:
:param inner_iteration:
:param sgd_iteration:
:param display: Print training progress
:param prefix:
:param prior:
:param dimension:
:param loss_type:
:param cost_type: Cost metric type
:param ot_method: Optimal transport solver method
:param lr: Learning rate
:param gamma:
:param max_cpu: Max CPU threads - 0 for unlimited
"""
alg = _GWL(
pair, epochs=epochs, batch_size=batch_size, use_cuda=use_cuda, strategy=strategy,
beta=beta, outer_iteration=outer_iteration, inner_iteration=inner_iteration,
sgd_iteration=sgd_iteration, display=display, prefix=prefix, prior=prior,
dimension=dimension, loss_type=loss_type, cost_type=cost_type, ot_method=ot_method,
lr=lr, gamma=gamma, max_cpu=max_cpu,
)
_warn_unused(alg.name, **kwargs)
return alg
[docs]
def sgwl(
pair: _GraphPair,
mn: int,
loss_type: str = "L2",
ot_method: str = "proximal",
beta: float = 0.2,
iter_bound: float = 1e-10,
inner_iteration: int = 2,
sk_bound: float = 1e-30,
node_prior: float = 1000,
max_iter: int = 4,
cost_bound: float = 1e-26,
update_p: bool = False,
lr: float = 0,
alpha: float = 1,
clus: int = 2,
level: int = 3,
**kwargs: Any,
) -> _SGWL:
"""
SGWL (Scalable Gromov-Wasserstein Learning)
:param pair: Graph Pair object
:param mn:
:param loss_type:
:param ot_method:
:param beta:
:param iter_bound:
:param inner_iteration:
:param sk_bound:
:param node_prior:
:param max_iter:
:param cost_bound:
:param update_p:
:param lr:
:param alpha:
:param clus:
:param level:
"""
alg = _SGWL(
pair,
mn=mn,
loss_type=loss_type,
ot_method=ot_method,
beta=beta,
iter_bound=iter_bound,
inner_iteration=inner_iteration,
sk_bound=sk_bound,
node_prior=node_prior,
max_iter=max_iter,
cost_bound=cost_bound,
update_p=update_p,
lr=lr,
alpha=alpha,
clus=clus,
level=level,
)
_warn_unused(alg.name, **kwargs)
return alg
[docs]
def path(pair: _GraphPair, **kwargs: Any) -> _Path:
"""
Path
:param pair: Graph Pair object
"""
alg = _Path(pair)
_warn_unused(alg.name, **kwargs)
return alg
[docs]
def isorank(
pair: _GraphPair,
L: object = None,
lalpha: float = 1.0,
weighted: bool = True,
alpha: float = 0.5,
tol: float = 1e-12,
maxiter: int = 1,
**kwargs: Any,
) -> _Isorank:
"""
IsoRank
:param pair: Graph Pair object
:param L: Pre-computed similarity matrix (default None, computed from lalpha)
:param lalpha: Degree-based similarity scaling factor (default 1.0)
:param weighted: Whether to use weighted similarity (default True)
:param alpha: Teleportation probability (default 0.5)
:param tol: Convergence tolerance (default 1e-12)
:param maxiter: Maximum iterations (default 1)
"""
alg = _Isorank(pair, L=L, lalpha=lalpha, weighted=weighted, alpha=alpha, tol=tol, max_iter=maxiter)
_warn_unused(alg.name, **kwargs)
return alg
[docs]
def nsd(pair: _GraphPair, alpha: float = 0.5, iters: int = 5, **kwargs: Any) -> _NSD:
"""
NSD (Network Similarity Decomposition)
:param pair: Graph Pair object
:param alpha: Damping factor (default 0.5)
:param iters: Number of iterations (default 5)
"""
alg = _NSD(pair, alpha=alpha, iters=iters)
_warn_unused(alg.name, **kwargs)
return alg
[docs]
def mmnc(pair: _GraphPair, train_ratio: float = 0.04, k_de: int = 3, k_nei: int = 7, t: int = 10, fast_select: bool = False, **kwargs: Any) -> _Mmnc:
"""
MMNC (Multi-order Matched Neighborhood Consistent Graph Alignment in a Union Vector Space)
:param pair:
:param train_ratio:
:param k_de:
:param k_nei:
:param t:
:param fast_select:
:param kwargs:
:return:
"""
alg = _Mmnc(pair, train_ratio, k_de, k_nei, t, fast_select)
_warn_unused(alg.name, **kwargs)
return alg
[docs]
def gradp(pair: _GraphPair, anchor_links: list | None = None, **kwargs: Any) -> _GradP:
"""
GradP (Grad-Align+)
:param pair: Graph Pair object
:param anchor_links: Lost of anchor pairs in src and target graphs (optional)
"""
if anchor_links is not None:
anchors_src = anchor_links[:, 0].tolist() # source node indices
anchors_tar = anchor_links[:, 1].tolist() # target node indices
else:
anchors_src, anchors_tar = None, None
alg = _GradP(pair, anchors_src=anchors_src, anchors_tar=anchors_tar)
_warn_unused(alg.name, **kwargs)
return alg
[docs]
def dspp(pair, **kwargs) -> _DSPP:
"""
DSPP (DS++)
:param pair: Graph Pair object
"""
alg = _DSPP(pair=pair)
_warn_unused(alg.name, **kwargs)
return alg
[docs]
def fgot(pair: _GraphPair, tau: int = 1, n_samples: int = 5, epochs: int = 1000, lr: int = 1, std_init: int = 5,
loss_type: str = 'w_simple', seed: int = 42, verbose:bool = True, tol: float = 1e-12, adapt_lr: bool = False, **kwargs):
"""
FGOT (Graph Distances Based on Filters and Optimal Transport)
:param pair:
:param tau:
:param n_samples:
:param epochs:
:param lr:
:param std_init:
:param loss_type:
:param seed:
:param verbose:
:param tol:
:param adapt_lr:
:return:
"""
alg = _FGot(pair, tau, n_samples, epochs, lr, std_init, loss_type, seed, verbose, tol, adapt_lr)
_warn_unused(alg.name, **kwargs)
return alg
[docs]
def got(pair: _GraphPair, it: int = 20, n_samples: int = 20, epochs: int = 10,
lr: float = 0.1, alpha: float = 0.1, ones: bool = True, loss_type: str = 'w',
seed: int = 42, verbose: bool = False, **kwargs):
"""
GOT (An Optimal Transport framework for Graph comparison)
:param pair: Graph Pair object
:param it:
:param n_samples:
:param epochs:
:param lr:
:param alpha:
:param ones:
:param loss_type:
:param seed:
:param verbose:
"""
alg = _Got(pair, it, n_samples, epochs, lr, alpha, ones, loss_type, seed, verbose)
_warn_unused(alg.name, **kwargs)
return alg
[docs]
def grasp(pair: _GraphPair, n_eig: int = 100, q: int = 20, k: int = 20, laa: int = 3,
icp: bool = True, icp_its: int = 3, lower_t: float = 0.1, upper_t: float = 50,
linsteps: bool = True, base_align: bool = True, **kwargs) -> _Grasp:
"""
GRASP (Scalable Graph Alignment by Spectral Corresponding Functions)
:param pair:
:param n_eig:
:param q:
:param k:
:param laa:
:param icp:
:param icp_its:
:param lower_t:
:param upper_t:
:param linsteps:
:param base_align:
:param kwargs:
:return:
"""
alg = _Grasp(pair, n_eig, q, k, laa, icp, icp_its, lower_t, upper_t, linsteps, base_align)
_warn_unused(alg.name, **kwargs)
return alg
[docs]
def klaus(pair: _GraphPair, a: int = 1, b: int = 1, gamma:float = 0.4, stepm:int = 25,
rtype: int = 1, maxiter: int = 1000, verbose: bool = False, **kwargs) -> _Klaus:
"""
KLAUS
:param pair:
:param a:
:param b:
:param gamma:
:param stepm:
:param rtype:
:param maxiter:
:param verbose:
:param kwargs:
:return:
"""
alg = _Klaus(pair, a, b, gamma, stepm, rtype, maxiter, verbose)
_warn_unused(alg.name, **kwargs)
return alg
[docs]
def lera(pair: _GraphPair, iters: int = 30, method: str = "lowrank_svd_union",
b_match: int = 1, default_params: bool = True, **kwargs) -> _Lrea:
"""
LERA (Low Rank Spectral Network Alignment)
:param pair:
:param iters:
:param method:
:param b_match:
:param default_params:
:param kwargs:
:return:
"""
alg = _Lrea(pair, iters, method, b_match, default_params)
_warn_unused(alg.name, **kwargs)
return alg
[docs]
def mds(pair: _GraphPair, n_components: int = 10, alpha: float = 0.05,
max_iter: int = 600, eps: float = 0.01, tol:float = 1e-3,
min_eps: float = 0.001, eps_annealing: bool = True, alpha_annealing: bool = True,
gw_init: bool = True, return_stress: bool = False, **kwargs) -> _Mds:
"""
MDS (Unsupervised Manifold Alignment with Joint Multidimensional Scaling)
:param pair:
:param n_components:
:param alpha:
:param max_iter:
:param eps:
:param tol:
:param min_eps:
:param eps_annealing:
:param alpha_annealing:
:param gw_init:
:param return_stress:
:param kwargs:
:return:
"""
alg = _Mds(pair, n_components, alpha, max_iter, eps, tol, min_eps, eps_annealing, alpha_annealing, gw_init, return_stress)
_warn_unused(alg.name, **kwargs)
return alg
[docs]
def net_align(pair: _GraphPair, a: int = 1, b: int = 1, gamma: float = 0.5,
dtype:int = 0, maxiter:int = 100, **kwargs) -> _NetAlign:
"""
Net-Align
:param pair:
:param a:
:param b:
:param gamma:
:param dtype:
:param maxiter:
:param kwargs:
:return:
"""
alg = _NetAlign(pair, a, b, gamma, dtype, maxiter)
_warn_unused(alg.name, **kwargs)
return alg
[docs]
def parrot(pair: _GraphPair, sep_rwr_iter: int = 100, prod_rwr_iter: int = 20,
alpha: float = 0.5, beta: float = 0.15, gamma: float = 0.5,
in_iter: int = 50, out_iter: int = 20,
l1: float = 0.1, l2: float = 0.1, l3: float = 0.1, l4: float = 0.01, **kwargs) -> _Parrot:
"""
PARROT (Position-Aware Regularized Optimal Transport for Network Alignment)
:param pair:
:param sep_rwr_iter:
:param prod_rwr_iter:
:param alpha:
:param beta:
:param gamma:
:param in_iter:
:param out_iter:
:param l1:
:param l2:
:param l3:
:param l4:
:param kwargs:
:return:
"""
alg = _Parrot(pair, sep_rwr_iter, prod_rwr_iter, alpha, beta, gamma, in_iter, out_iter, l1, l2, l3, l4)
_warn_unused(alg.name, **kwargs)
return alg
[docs]
def regal(pair: _GraphPair, attributes: str | None = None, numtop: int = 10,
alpha: float = 0.01, buckets: float = 2, k: int = 10, gammastruc: float = 1.0,
gammaattr: float = 1.0, untillayer: int = 2, **kwargs) -> _Regal:
"""
REGAL (Representation Learning-based Graph Alignment)
:param pair: Graph Pair object
:param attributes: Path to a saved numpy matrix of node attributes (None for structural only)
:param numtop: Number of top similarities to keep via KD-tree (0 = all pairwise)
:param alpha: Discount factor for further layers
:param buckets: Base of log for degree binning (1 = no log binning)
:param k: Controls the number of landmarks to sample (d = k*log(n))
:param gammastruc: Weight on structural similarity
:param gammaattr: Weight on attribute similarity
:param untillayer: Calculate xNetMF until this layer (0 = no limit)
"""
alg = _Regal(pair, attributes, numtop, alpha, buckets, k, gammastruc, gammaattr, untillayer)
_warn_unused(alg.name, **kwargs)
return alg
[docs]
def joena(pair: _GraphPair,
anchor_links: np.ndarray,
hidden_dim: int = 128,
out_dim: int = 128,
alpha: float = 0.9,
gamma_p: float = 1e-2,
in_iter: int = 5,
out_iter: int = 10,
init_threshold_lambda: float = 1.0,
lr: float = 1e-3,
epochs: int = 30, runs: int = 1, device: str = 'cpu', **kwargs) -> _Joena:
"""
JOENA (Joint Optimal Transport and Embedding for Network Alignment)
:param pair:
:param anchor_links:
:param hidden_dim:
:param out_dim:
:param alpha:
:param gamma_p:
:param in_iter:
:param out_iter:
:param init_threshold_lambda:
:param lr:
:param epochs:
:param runs:
:param device:
:return:
"""
alg = _Joena(pair, anchor_links, hidden_dim, out_dim, alpha, gamma_p, in_iter, out_iter, init_threshold_lambda, lr, epochs, runs, device)
_warn_unused(alg.name, **kwargs)
return alg
[docs]
def htc(pair: _GraphPair, src_laps: torch.Tensor | None = None,
trg_laps: torch.Tensor | None = None, hid_dim: int = 500, num_utrn: int = 15,
ulr: float = 0.01, num_ftune: int = 25, flr: float = 0.01, alpha: float = 1.1,
k: int = 40, p: float = 0.5, graphlet_size: int = 4, **kwargs) -> _Htc:
"""
HTC (Towards Higher-order Topological Consistency for Unsupervised Network Alignment)
:param pair: Graph Pair object
:param src_laps: Precomputed source graphlet-orbit built from pair.src when None.
:param trg_laps: Precomputed target graphlet-orbit built from pair.tar when None.
:param hid_dim:
:param num_utrn:
:param ulr:
:param num_ftune:
:param flr:
:param alpha:
:param k:
:param p:
:param graphlet_size:
"""
alg = _Htc(pair, hid_dim, num_utrn, ulr, num_ftune, flr, alpha, k, p,
src_laps=src_laps, trg_laps=trg_laps, graphlet_size=graphlet_size)
_warn_unused(alg.name, **kwargs)
return alg
[docs]
def slot_a(pair: _GraphPair, step_size: float = 0.1, bases: int = 2,
joint_epoch: int = 100, epoch: int = 100, gw_beta: float = 0.01, **kwargs):
"""
SlotA
:param pair:
:param step_size:
:param bases:
:param joint_epoch:
:param epoch:
:param gw_beta:
:return:
"""
alg = _SlotA(pair, step_size, bases, joint_epoch, epoch, gw_beta)
_warn_unused(alg.name, **kwargs)
return alg
[docs]
def alpine(pair: _GraphPair, anchor_links: Union[List[Tuple[int, int]], None] = None,k: Union[int, None] = None, connected: bool = False, mu: Union[float, None] = None, niter: int = 10, sinkhorn_iters: int = 500, sinkhorn_tol: float = 1e-9, method: Literal['FW', 'AMD'] = 'FW', **kwargs) -> _ALPINE:
"""
:param pair:
:param anchor_links:
:param k:
:param connected:
:param mu:
:param niter:
:param sinkhorn_iters:
:param sinkhorn_tol:
:param method:
:return:
"""
alg = _ALPINE(pair, anchor_links, k, connected, mu, niter, sinkhorn_iters, sinkhorn_tol, method)
_warn_unused(alg.name, **kwargs)
return alg
def next_align(pair: _GraphPair, anchor_links: np.ndarray, dim: int = 128, num_layer: int = 1,
coeff1: float = 1.0, coeff2: float = 1.0, lr: float = 0.01, epochs: int = 100, batch_size: int = 300,
walks_num: int = 100, N_steps: int = 10, N_negs: int = 20, p: int = 1, q: int = 1, walk_length: int = 80,
num_walks: int = 10, dist: str = 'L1', device: str = 'cpu', **kwargs) -> object: #_NextAlign:
"""
Next Align, skipped due to DGL compatibility issues
:param pair:
:param anchor_links:
:param dim:
:param num_layer:
:param coeff1:
:param coeff2:
:param lr:
:param epochs:
:param batch_size:
:param walks_num:
:param N_steps:
:param N_negs:
:param p:
:param q:
:param walk_length:
:param num_walks:
:param dist:
:param device:
:param kwargs:
:return:
"""
raise NotImplementedError
alg = _NextAlign(pair, anchor_links, dim, num_layer, coeff1, coeff2, lr, epochs, batch_size, walks_num, N_steps, N_negs, p, q, walk_length, num_walks, dist, device)
_warn_unused(alg.name, **kwargs)
return alg