Download julia/tda_features.jl from Snapkitty/quantum-kernel: direct link, hf CLI and curl.
- Browser
- Download file 7.51 kB
-
https://huggingface.co/Snapkitty/quantum-kernel/resolve/main/julia/tda_features.jl
- Command line
-
hf download hf://Snapkitty/quantum-kernel/julia/tda_features.jl
-
curl -L -o tda_features.jl https://huggingface.co/Snapkitty/quantum-kernel/resolve/main/julia/tda_features.jl
7.51 kB
| # tda_features.jl β Vietoris-Rips β Persistence Barcodes β Feature Vectors | |
| module TDAFeatures | |
| using LinearAlgebra | |
| using Statistics | |
| export VietorisRipsComplex, PersistenceDiagram, Barcode, barcode_to_feature_vector | |
| export compute_persistence, wasserstein_distance, bottleneck_distance | |
| export PersistenceInterval | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # Vietoris-Rips Complex | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| struct VietorisRipsComplex | |
| points::Matrix{Float64} | |
| max_dim::Int | |
| epsilon::Float64 | |
| simplices::Vector{Vector{Int}} | |
| filtration_values::Vector{Float64} | |
| end | |
| function VietorisRipsComplex(points::Matrix{Float64}, epsilon::Float64; max_dim::Int=2) | |
| n = size(points, 1) | |
| simplices = Vector{Int}[] | |
| filt_vals = Float64[] | |
| for i in 1:n | |
| push!(simplices, [i]) | |
| push!(filt_vals, 0.0) | |
| end | |
| for i in 1:n, j in i+1:n | |
| d = norm(points[i,:] - points[j,:]) | |
| if d <= epsilon | |
| push!(simplices, [i, j]) | |
| push!(filt_vals, d) | |
| end | |
| end | |
| if max_dim >= 2 | |
| for i in 1:n, j in i+1:n, k in j+1:n | |
| d_ij = norm(points[i,:] - points[j,:]) | |
| d_jk = norm(points[j,:] - points[k,:]) | |
| d_ik = norm(points[i,:] - points[k,:]) | |
| if d_ij <= epsilon && d_jk <= epsilon && d_ik <= epsilon | |
| push!(simplices, [i, j, k]) | |
| push!(filt_vals, max(d_ij, d_jk, d_ik)) | |
| end | |
| end | |
| end | |
| VietorisRipsComplex(points, max_dim, epsilon, simplices, filt_vals) | |
| end | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # Persistent Homology (H0 and H1) | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| struct PersistenceInterval | |
| dim::Int | |
| birth::Float64 | |
| death::Float64 | |
| end | |
| struct PersistenceDiagram | |
| intervals::Vector{PersistenceInterval} | |
| end | |
| struct Barcode | |
| H0::Vector{PersistenceInterval} | |
| H1::Vector{PersistenceInterval} | |
| end | |
| function compute_persistence(vr::VietorisRipsComplex)::Barcode | |
| n = size(vr.points, 1) | |
| order = sortperm(vr.filtration_values) | |
| # H0: Connected components (union-find) | |
| parent = collect(1:n) | |
| rank = zeros(Int, n) | |
| function find(x) | |
| while parent[x] != x | |
| parent[x] = parent[parent[x]] | |
| x = parent[x] | |
| end | |
| return x | |
| end | |
| function union!(x, y) | |
| rx, ry = find(x), find(y) | |
| if rx != ry | |
| if rank[rx] < rank[ry] | |
| parent[rx] = ry | |
| elseif rank[rx] > rank[ry] | |
| parent[ry] = rx | |
| else | |
| parent[ry] = rx | |
| rank[rx] += 1 | |
| end | |
| return true | |
| end | |
| return false | |
| end | |
| H0_intervals = PersistenceInterval[] | |
| for idx in order | |
| simp = vr.simplices[idx] | |
| val = vr.filtration_values[idx] | |
| if length(simp) == 2 | |
| if union!(simp[1], simp[2]) | |
| push!(H0_intervals, PersistenceInterval(0, 0.0, val)) | |
| end | |
| end | |
| end | |
| max_filt = maximum(vr.filtration_values) | |
| for i in 1:n | |
| if find(i) == i | |
| push!(H0_intervals, PersistenceInterval(0, 0.0, max_filt)) | |
| end | |
| end | |
| # H1: Cycles | |
| H1_intervals = PersistenceInterval[] | |
| parent_h1 = collect(1:n) | |
| function find_h1(x) | |
| while parent_h1[x] != x | |
| parent_h1[x] = parent_h1[parent_h1[x]] | |
| x = parent_h1[x] | |
| end | |
| return x | |
| end | |
| function union_h1!(x, y) | |
| rx, ry = find_h1(x), find_h1(y) | |
| if rx != ry | |
| parent_h1[rx] = ry | |
| return false | |
| end | |
| return true | |
| end | |
| for idx in order | |
| simp = vr.simplices[idx] | |
| val = vr.filtration_values[idx] | |
| if length(simp) == 2 | |
| if union_h1!(simp[1], simp[2]) | |
| push!(H1_intervals, PersistenceInterval(1, val, max_filt)) | |
| end | |
| end | |
| end | |
| Barcode(H0_intervals, H1_intervals) | |
| end | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # Barcode β Feature Vector | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| function barcode_to_feature_vector(bc::Barcode; n_bins::Int=50, max_filt::Float64=1.0)::Vector{Float64} | |
| features = Float64[] | |
| for intervals in [bc.H0, bc.H1] | |
| if isempty(intervals) | |
| append!(features, zeros(n_bins)) | |
| continue | |
| end | |
| landscape = zeros(n_bins) | |
| for intv in intervals | |
| mid = (intv.birth + intv.death) / 2 | |
| half_pers = (intv.death - intv.birth) / 2 | |
| for (i, t) in enumerate(range(0, max_filt, length=n_bins)) | |
| val = max(0.0, half_pers - abs(t - mid)) | |
| landscape[i] = max(landscape[i], val) | |
| end | |
| end | |
| append!(features, landscape) | |
| end | |
| push!(features, Float64(length(bc.H0))) | |
| push!(features, Float64(length(bc.H1))) | |
| push!(features, sum(i.death - i.birth for i in bc.H0)) | |
| push!(features, sum(i.death - i.birth for i in bc.H1)) | |
| push!(features, maximum([i.death - i.birth for i in bc.H1]; init=0.0)) | |
| return features | |
| end | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # Distances Between Barcodes | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| function wasserstein_distance(bc1::Barcode, bc2::Barcode; p::Int=2)::Float64 | |
| dist = 0.0 | |
| for (intervals1, intervals2) in [(bc1.H0, bc2.H0), (bc1.H1, bc2.H1)] | |
| n1, n2 = length(intervals1), length(intervals2) | |
| if n1 == 0 && n2 == 0 | |
| continue | |
| elseif n1 == 0 | |
| dist += sum((i.death - i.birth)^p for i in intervals2) | |
| elseif n2 == 0 | |
| dist += sum((i.death - i.birth)^p for i in intervals1) | |
| else | |
| sorted1 = sort(intervals1, by=i -> i.death - i.birth, rev=true) | |
| sorted2 = sort(intervals2, by=i -> i.death - i.birth, rev=true) | |
| for (i1, i2) in zip(sorted1, sorted2) | |
| dist += abs((i1.death - i1.birth) - (i2.death - i2.birth))^p | |
| end | |
| end | |
| end | |
| return dist^(1/p) | |
| end | |
| function bottleneck_distance(bc1::Barcode, bc2::Barcode)::Float64 | |
| wasserstein_distance(bc1, bc2; p=100) | |
| end | |
| end # module TDAFeatures | |