/
diagrams.jl
137 lines (119 loc) · 4.21 KB
/
diagrams.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
"""
PersistenceDiagram <: AbstractVector{PersistenceInterval}
Type for representing persistence diagrams. Behaves exactly like a vector of
`PersistenceInterval`s, but can have additional metadata attached to it. It supports pretty
printing and plotting.
Can be used as a table with any function that uses the
[`Tables.jl`](https://github.com/JuliaData/Tables.jl) interface. Note that using it as a
table will only keep interval endpoints and the `dim` and `threshold` attributes.
# Example
```jldoctest
julia> diagram = PersistenceDiagram([(1, 3), (3, 4), (1, Inf)]; dim=1, custom_metadata=:a)
3-element 1-dimensional PersistenceDiagram:
[1.0, 3.0)
[3.0, 4.0)
[1.0, ∞)
julia> diagram[1]
[1.0, 3.0)
julia> sort(diagram; by=persistence, rev=true)
3-element 1-dimensional PersistenceDiagram:
[1.0, ∞)
[1.0, 3.0)
[3.0, 4.0)
julia> propertynames(diagram)
(:intervals, :dim, :custom_metadata)
julia> dim(diagram)
1
julia> diagram.custom_metadata
:a
```
"""
struct PersistenceDiagram <: AbstractVector{PersistenceInterval}
intervals::Vector{PersistenceInterval}
meta::NamedTuple
end
function PersistenceDiagram(intervals::Vector{PersistenceInterval}; kwargs...)
meta = (; kwargs...)
return PersistenceDiagram(intervals, meta)
end
function PersistenceDiagram(intervals::AbstractVector{PersistenceInterval}; kwargs...)
return PersistenceDiagram(collect(intervals); kwargs...)
end
function PersistenceDiagram(pairs::AbstractVector{<:Tuple}; kwargs...)
return PersistenceDiagram(PersistenceInterval.(pairs); kwargs...)
end
function PersistenceDiagram(table)
rows = Tables.rows(table)
if isempty(rows)
return PersistenceDiagram(PersistenceInterval[])
else
firstrow = first(rows)
dim = hasproperty(firstrow, :dim) ? firstrow.dim : missing
threshold = hasproperty(firstrow, :threshold) ? firstrow.threshold : missing
intervals = map(rows) do row
d = hasproperty(row, :dim) ? row.dim : missing
t = hasproperty(row, :threshold) ? row.threshold : missing
if !isequal(d, dim)
error("different `dim`s detected. Try splitting the table first.")
end
if !isequal(t, threshold)
error("different `threshold`s detected. Try splitting the table first.")
end
PersistenceInterval(row.birth, row.death)
end
return PersistenceDiagram(intervals; dim=dim, threshold=threshold)
end
end
function Base.show(io::IO, diag::PersistenceDiagram)
return summary(io, diag)
end
function Base.summary(io::IO, diag::PersistenceDiagram)
if haskey(diag.meta, :dim)
print(io, length(diag), "-element ", dim(diag), "-dimensional PersistenceDiagram")
else
print(io, length(diag), "-element PersistenceDiagram")
end
end
###
### Array interface
###
Base.size(diag::PersistenceDiagram) = size(diag.intervals)
Base.getindex(diag::PersistenceDiagram, i::Integer) = diag.intervals[i]
Base.setindex!(diag::PersistenceDiagram, x, i::Integer) = diag.intervals[i] = x
Base.firstindex(diag::PersistenceDiagram) = 1
Base.lastindex(diag::PersistenceDiagram) = length(diag.intervals)
function Base.similar(diag::PersistenceDiagram)
return PersistenceDiagram(similar(diag.intervals); diag.meta...)
end
function Base.similar(diag::PersistenceDiagram, dims::Tuple)
return PersistenceDiagram(similar(diag.intervals, dims); diag.meta...)
end
###
### Meta
###
function Base.getproperty(diag::PersistenceDiagram, key::Symbol)
if hasfield(typeof(diag), key)
return getfield(diag, key)
elseif haskey(diag.meta, key)
return diag.meta[key]
else
error("$diag has no $key")
end
end
function Base.propertynames(diag::PersistenceDiagram, private::Bool=false)
if private
return tuple(:intervals, :meta, propertynames(diag.meta)...)
else
return tuple(:intervals, propertynames(diag.meta)...)
end
end
"""
threshold(diagram::PersistenceDiagram)
Get the threshold of persistence diagram. Equivalent to `diagram.threshold`.
"""
threshold(diag::PersistenceDiagram) = diag.threshold
"""
dim(diagram::PersistenceDiagram)
Get the dimension of persistence diagram. Equivalent to `diagram.dim`.
"""
dim(diag::PersistenceDiagram) = diag.dim