Skip to main content
  • Home
  • Development
  • Documentation
  • Donate
  • Operational login
  • Browse the archive

swh logo
SoftwareHeritage
Software
Heritage
Archive
Features
  • Search

  • Downloads

  • Save code now

  • Add forge now

  • Help

https://github.com/tiga1231/sgd2
08 April 2026, 17:08:34 UTC
  • Code
  • Branches (1)
  • Releases (0)
  • Visits
    • Branches
    • Releases
    • HEAD
    • refs/heads/main
    No releases to show
  • 44ecc7e
  • /
  • utils
  • /
  • utils.py
Raw File Download Save again
Take a new snapshot of a software origin

If the archived software origin currently browsed is not synchronized with its upstream version (for instance when new commits have been issued), you can explicitly request Software Heritage to take a new snapshot of it.

Use the form below to proceed. Once a request has been submitted and accepted, it will be processed as soon as possible. You can then check its processing state by visiting this dedicated page.
swh spinner

Processing "take a new snapshot" request ...

To reference or cite the objects present in the Software Heritage archive, permalinks based on SoftWare Hash IDentifiers (SWHIDs) must be used.
Select below a type of object currently browsed in order to display its associated SWHID and permalink.

  • content
  • directory
  • revision
  • snapshot
origin badgecontent badge
swh:1:cnt:200f5c107cd572b4681a807c88b79ec95c8f20b3
origin badgedirectory badge
swh:1:dir:ca52be72fa7bff082737dff0201a7cf2ceef2d06
origin badgerevision badge
swh:1:rev:13ca3d978626473e59fdddd641e457edc57208bc
origin badgesnapshot badge
swh:1:snp:139ff70469db38efd8ca16259dcc2fc37237cb74

This interface enables to generate software citations, provided that the root directory of browsed objects contains a citation.cff or codemeta.json file.
Select below a type of object currently browsed in order to generate citations for them.

  • content
  • directory
  • revision
  • snapshot
(requires biblatex-software package)
Generating citation ...
(requires biblatex-software package)
Generating citation ...
(requires biblatex-software package)
Generating citation ...
(requires biblatex-software package)
Generating citation ...
Tip revision: 13ca3d978626473e59fdddd641e457edc57208bc authored by jack on 02 March 2022, 18:24:49 UTC
update OS note
Tip revision: 13ca3d9
utils.py
import torch
from torch import nn


import numpy as np
import time
import utils.poly_point_isect as bo   ##bentley-ottmann sweep line
import networkx as nx

from scipy.sparse import csgraph
from scipy.sparse import csr_matrix
import scipy.io as io

def best_scale_stress(pos, D, W):
    n,m = pos.shape[0], pos.shape[1]
    x0 = pos.repeat(1, n).view(-1,m)
    x1 = pos.repeat(n, 1)
    D = D.view(-1)
    W = W.view(-1)
    pdist = nn.PairwiseDistance()(x0, x1)
    s = (W*D*pdist).sum() / (W * pdist**2).sum()
    return s


def best_scale_ideal_edge_length(pos, G, k2i, targetLengths=None):
    if targetLengths is None:
        targetLengths = {e:1 for e in G.edges}
        
    edges = G.edges
    sourceIndices, targetIndices = zip(*[ [k2i[e0], k2i[e1]] for e0,e1 in edges])
    source = pos[sourceIndices,:]
    target = pos[targetIndices,:]
    edgeLengths = (source-target).norm(dim=1)
    targetLengths = torch.tensor([targetLengths[e] for e in edges])

    s = (edgeLengths-targetLengths).sum() / (edgeLengths-targetLengths).pow(2).sum()
    
    return s
    
    
    
def criterion_to_title(criterion):
    return ' '.join([x.capitalize() for x in criterion.split('_')])


def are_edge_pairs_crossed(p):
    '''
    p - positions of n pairs edges in a [n,8] pytorch tensor, 
        where the postions of 8 nodes come in [ax, ay, bx, by, 
        cx, cy, dy, dy] for the edge pair a-b and c-d.
    
    return - an 1D tensor of boolean values, 
             where True means two edges cross each other. 
    '''
    p1, p2, p3, p4 = p[:,:2], p[:,2:4], p[:,4:6], p[:,6:]
    a = p2 - p1
    b = p3 - p4
    c = p1 - p3
    ax, ay = a[:,0], a[:,1]
    bx, by = b[:,0], b[:,1]
    cx, cy = c[:,0], c[:,1]
    
    denom = ay*bx - ax*by
    numer_alpha = by*cx-bx*cy
    numer_beta = ax*cy-ay*cx
    alpha = numer_alpha / denom
    beta = numer_beta / denom
    return torch.logical_and(
        torch.logical_and(0<alpha, alpha<1),
        torch.logical_and(0<beta, beta<1),
    )

def shortest_path(G):
    k2i = {k:i for i,k in enumerate(G.nodes)}
    edge_indices = np.array([(k2i[n0], k2i[n1]) for (n0,n1) in G.edges])
    row_indices = edge_indices[:,0]
    col_indices = edge_indices[:,1]
    adj_data = np.ones(len(edge_indices))
    adj_sparse = csr_matrix((
        adj_data, 
        (row_indices, col_indices)
    ), shape=(len(G), len(G)), dtype=np.float32)

    D = csgraph.shortest_path(adj_sparse, directed=False, unweighted=True)
    return D, adj_sparse, k2i



def load_spx_teaser():
    return load_node_link_txt('./input_graphs/spx_teaser.txt')
    
    
def load_node_link_txt(fn):
    
    def skip_nodes(f,n):
        for _ in range(n):
            f.readline()

    G = nx.Graph()
    with open(fn) as f:
        n = int(f.readline().strip())
        skip_nodes(f, n)
        G.add_nodes_from(range(n))
        for e in f:
            e = e.strip().split()
            e = int(e[0]), int(e[1])
            G.add_edge(*e)
    return G


def load_mat(fn='input_graphs/SuiteSparse Matrix Collection/grid1_dual.mat'):
    
    ## credit:
    ## https://github.com/jxz12/s_gd2/blob/master/jupyter/main.ipynb

    # load the data from the SuiteSparse Matrix Collection format
    # https://www.cise.ufl.edu/research/sparse/matrices/

    mat_data = io.loadmat(fn)
    adj = mat_data['Problem']['A'][0][0]
    G = nx.convert_matrix.from_numpy_matrix(adj.toarray())
    return G




def get_angles(rays):
    x,y = rays[:,0], rays[:,1]
    thetas = torch.angle(x+y*1j)
    thetas_sorted = torch.sort(thetas).values
    angles = thetas_sorted.roll(-1) - thetas_sorted
    angles[-1] *= -1
#     angles[angles>np.pi] = 2*np.pi - angles[angles>np.pi]
    return angles

    
def tick(t0, msg=''):
    t1 = time.time()
    print(f'{msg}: {t1-t0}')
    return t1

def sample_edges(G, sampleSize):
    edge_list = list(G.edges)
    sample_indices = np.random.choice(len(edge_list), min(sampleSize,len(edge_list)), replace=False)
    edge_samples = [edge_list[i] for i in sample_indices]
    return edge_samples

def sample_nodes(G, sampleSize):
    node_list = list(G.nodes)
    sample_indices = np.random.choice(len(node_list), min(sampleSize,len(node_list)), replace=False)
    node_samples = [node_list[i] for i in sample_indices]
    return node_samples

def sample_crossings(pos, G, k2i, sampleSize, sampleOn):
    edge_list = list(G.edges)
    if sampleOn == 'edges':
        sample_indices = np.random.choice(len(edge_list), min(sampleSize,len(edge_list)), replace=False)
        edge_samples = [edge_list[i] for i in sample_indices]
        crossing_segs_sample = find_crossings(pos, edge_samples, k2i)
        
    elif sampleOn == 'crossings':
        crossing_segs = find_crossings(pos, edge_list, k2i)
        crossing_count = crossing_segs.shape[0]
        sample_indices = np.random.choice(crossing_count, min(sampleSize,crossing_count), replace=False)
        crossing_segs_sample = crossing_segs[sample_indices]
    return crossing_segs_sample
    
    

def find_crossings(pos, G_edges, k2i):
    
    ##TODO improve runtime
    t0 = time.time()
    x = pos.detach().cpu().numpy().tolist()
#     t0 = tick(t0, 'a')
    x = [(
            tuple([*x[k2i[e0]], k2i[e0]]), ## (x, y, source id)
            tuple([*x[k2i[e1]], k2i[e1]])  ## (x, y, target id) 
        )
        for e0,e1 in G_edges
    ]
#     t0 = tick(t0, 'b')
 
    ## option 1
    point_segs_pairs = bo.isect_segments_include_segments(x)
    crossing_segs = np.array([psp[1][:2] for psp in point_segs_pairs])##select first [:2] edges whenever more than 2 edges crossed at the same intersection 
    
#     t0 = tick(t0, 'c')
    if len(crossing_segs) > 0:
        crossing_segs = crossing_segs[:,:,:,2].reshape([crossing_segs.shape[0], -1])
        crossing_segs = crossing_segs.astype(np.int)## indices of 4 nodes in edge crossing pairs
        return crossing_segs
    else:
        return np.zeros([0,4])

#     ## option 2 (faster)
#     crossing_segs = []
#     intersections = intersection(x)
#     if intersections is not None:
#         for l in list(intersections.values()):
#             a,b,c,d = l[0][0][2], l[0][1][2], l[1][0][2], l[1][1][2]
#             if a!=c and a!=d and b!=c and b!=d:
#                 crossing_segs.append([a,b,c,d])
#         crossing_segs = np.array(crossing_segs)
#         return crossing_segs
#     #     t0 = tick(t0, 'c')
#     else:
#         return np.zeros([0,4])

    
def count_crossings(pos, edge_pair_indices):

    x = pos.detach().cpu().numpy().tolist()
#     t0 = tick(t0, 'a')
    x = [(
            tuple([*x[e0]]), ## (x, y)
            tuple([*x[e1]]) 
        )
        for e0,e1 in edge_pair_indices
    ]
    return len(bo.isect_segments(x))


#https://discuss.pytorch.org/t/efficient-distance-matrix-computation/9065/3
def pairwise_distance_squared(x, y=None, w=None):
    '''
    Input: x is a Nxd matrix
           y is an optional Mxd matirx
    Output: dist is a NxM matrix where dist[i,j] is the square norm between x[i,:] and y[j,:]
            if y is not given then use 'y=x'.
    i.e. dist[i,j] = ||x[i,:]-y[j,:]||^2
    '''
    x_norm = (x**2).sum(1).view(-1, 1)
    if y is None:
        y = x
        y_t = y.t()
        y_norm = x_norm
    else:
        y_t = y.t()
        y_norm = (y**2).sum(1).view(1, -1)
        
    if w is not None:
        x = x * w    
        y = y * w    
    dist = x_norm + y_norm - 2.0 * torch.mm(x, y_t)
    return dist


def file2graph(fn='./facebook/0.edges'):
    with open(fn) as f:
        lines = [l.split()[:2] for l in f.readlines()]
        edges = [tuple(int(i) for i in l) for l in lines]
        nodes = set(sum(edges, ())) ## SLOW?
#         edges += [(-1, n) for n in nodes]
#         nodes.update({-1})
    G = nx.Graph()
    G.add_nodes_from(list(nodes))
    G.add_edges_from(edges)
    return G



def dict2tensor(d, device='cpu', fill=None):
    n = len(d.keys())
    k2i = {k:i for i,k in enumerate(sorted(d.keys()))}
    res = torch.zeros(len(d.keys()), len(d.keys()), device=device)
    for src_node, dst_nodes in d.items():
        for dst_node, distance in dst_nodes.items():
            if fill is not None:
                res[k2i[src_node],k2i[dst_node]] = fill
            else:
                res[k2i[src_node],k2i[dst_node]] = distance
    return res, k2i

back to top

Software Heritage — Copyright (C) 2015–2026, The Software Heritage developers. License: GNU AGPLv3+.
The source code of Software Heritage itself is available on our development forge.
The source code files archived by Software Heritage are available under their own copyright and licenses.
Terms of use: Archive access, API— Content policy— Contact— JavaScript license information— Web API