• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

MilesCranmer / SymbolicRegression.jl / 9704727222

27 Jun 2024 11:01PM UTC coverage: 95.922% (+1.3%) from 94.617%
9704727222

Pull #326

github

web-flow
Merge 1f104aaf8 into ceddaa424
Pull Request #326: BREAKING: Change expression types to `DynamicExpressions.Expression` (from `DynamicExpressions.Node`)

301 of 307 new or added lines in 17 files covered. (98.05%)

1 existing line in 1 file now uncovered.

2611 of 2722 relevant lines covered (95.92%)

35611300.15 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.0
/src/PopMember.jl
1
module PopMemberModule
2

3
using DispatchDoctor: @unstable
4
using DynamicExpressions:
5
    AbstractExpression,
6
    AbstractExpressionNode,
7
    copy_node,
8
    count_nodes,
9
    parse_expression,
10
    string_tree
11
using ..CoreModule: Options, Dataset, DATA_TYPE, LOSS_TYPE, create_expression
12
import ..ComplexityModule: compute_complexity
13
using ..UtilsModule: get_birth_order
14
using ..LossFunctionsModule: score_func
15

16
# Define a member of population by equation, score, and age
17
mutable struct PopMember{T<:DATA_TYPE,L<:LOSS_TYPE,N<:AbstractExpression{T}}
18
    tree::N
78,894,759✔
19
    score::L  # Inludes complexity penalty, normalization
20
    loss::L  # Raw loss
21
    birth::Int
22
    complexity::Int
23

24
    # For recording history:
25
    ref::Int
26
    parent::Int
27
end
28
function Base.setproperty!(member::PopMember, field::Symbol, value)
5,620,051✔
29
    field == :complexity && throw(
5,779,988✔
30
        error("Don't set `.complexity` directly. Use `recompute_complexity!` instead.")
31
    )
32
    field == :tree && setfield!(member, :complexity, -1)
5,779,276✔
33
    return setfield!(member, field, value)
5,797,665✔
34
end
35
@unstable @inline function Base.getproperty(member::PopMember, field::Symbol)
4,089,126,893✔
36
    field == :complexity && throw(
4,692,319,885✔
37
        error("Don't access `.complexity` directly. Use `compute_complexity` instead.")
38
    )
39
    return getfield(member, field)
5,395,714,452✔
40
end
41
function Base.show(io::IO, p::PopMember{T,L,N}) where {T,L,N}
18✔
42
    shower(x) = sprint(show, x)
54✔
43
    print(io, "PopMember(")
18✔
44
    print(io, "tree = (", string_tree(p.tree), "), ")
27✔
45
    print(io, "loss = ", shower(p.loss), ", ")
18✔
46
    print(io, "score = ", shower(p.score))
18✔
47
    print(io, ")")
18✔
48
    return nothing
18✔
49
end
50

51
generate_reference() = abs(rand(Int))
76,029,593✔
52

53
"""
54
    PopMember(t::AbstractExpression{T}, score::L, loss::L)
55

56
Create a population member with a birth date at the current time.
57
The type of the `Node` may be different from the type of the score
58
and loss.
59

60
# Arguments
61

62
- `t::AbstractExpression{T}`: The tree for the population member.
63
- `score::L`: The score (normalized to a baseline, and offset by a complexity penalty)
64
- `loss::L`: The raw loss to assign.
65
"""
66
function PopMember(
136,546,505✔
67
    t::AbstractExpression{T},
68
    score::L,
69
    loss::L,
70
    options::Union{Options,Nothing}=nothing,
71
    complexity::Union{Int,Nothing}=nothing;
72
    ref::Int=-1,
73
    parent::Int=-1,
74
    deterministic=nothing,
75
) where {T<:DATA_TYPE,L<:LOSS_TYPE}
76
    if ref == -1
87,521,785✔
77
        ref = generate_reference()
74,388,636✔
78
    end
79
    if !(deterministic isa Bool)
74,565,607✔
NEW
80
        throw(
×
81
            ArgumentError(
82
                "You must declare `deterministic` as `true` or `false`, it cannot be left undefined.",
83
            ),
84
        )
85
    end
86
    complexity = complexity === nothing ? -1 : complexity
74,494,405✔
87
    return PopMember{T,L,typeof(t)}(
74,563,308✔
88
        t,
89
        score,
90
        loss,
91
        get_birth_order(; deterministic=deterministic),
92
        complexity,
93
        ref,
94
        parent,
95
    )
96
end
97

98
"""
99
    PopMember(
100
        dataset::Dataset{T,L},
101
        t::AbstractExpression{T},
102
        options::Options
103
    )
104

105
Create a population member with a birth date at the current time.
106
Automatically compute the score for this tree.
107

108
# Arguments
109

110
- `dataset::Dataset{T,L}`: The dataset to evaluate the tree on.
111
- `t::AbstractExpression{T}`: The tree for the population member.
112
- `options::Options`: What options to use.
113
"""
114
function PopMember(
1,179,827✔
115
    dataset::Dataset{T,L},
116
    tree::Union{AbstractExpressionNode{T},AbstractExpression{T}},
117
    options::Options,
118
    complexity::Union{Int,Nothing}=nothing;
119
    ref::Int=-1,
120
    parent::Int=-1,
121
    deterministic=nothing,
122
) where {T<:DATA_TYPE,L<:LOSS_TYPE}
123
    ex = create_expression(tree, options, dataset)
1,079,530✔
124
    set_complexity = complexity === nothing ? compute_complexity(ex, options) : complexity
688,399✔
125
    @assert set_complexity != -1
589,375✔
126
    score, loss = score_func(dataset, ex, options; complexity=set_complexity)
880,469✔
127
    return PopMember(
589,324✔
128
        ex,
129
        score,
130
        loss,
131
        options,
132
        set_complexity;
133
        ref=ref,
134
        parent=parent,
135
        deterministic=deterministic,
136
    )
137
end
138

139
function Base.copy(p::P) where {P<:PopMember}
3,207,948✔
140
    tree = copy(p.tree)
6,520,913✔
141
    score = copy(p.score)
4,325,922✔
142
    loss = copy(p.loss)
4,324,762✔
143
    birth = copy(p.birth)
4,325,914✔
144
    complexity = copy(getfield(p, :complexity))
4,326,988✔
145
    ref = copy(p.ref)
4,327,412✔
146
    parent = copy(p.parent)
4,327,358✔
147
    return P(tree, score, loss, birth, complexity, ref, parent)
4,327,899✔
148
end
149

150
function reset_birth!(p::PopMember; deterministic::Bool)
76,449✔
151
    p.birth = get_birth_order(; deterministic)
98,156✔
152
    return p
41,898✔
153
end
154

155
# Can read off complexity directly from pop members
156
function compute_complexity(
3,100,598,341✔
157
    member::PopMember, options::Options; break_sharing=Val(false)
158
)::Int
159
    complexity = getfield(member, :complexity)
2,278,957,313✔
160
    complexity == -1 && return recompute_complexity!(member, options; break_sharing)
1,701,641,145✔
161
    # TODO: Turn this into a warning, and then return normal compute_complexity instead.
162
    return complexity
1,696,258,483✔
163
end
164
function recompute_complexity!(
5,893,697✔
165
    member::PopMember, options::Options; break_sharing=Val(false)
166
)::Int
167
    complexity = compute_complexity(member.tree, options; break_sharing)
4,240,727✔
168
    setfield!(member, :complexity, complexity)
3,194,341✔
169
    return complexity
3,186,574✔
170
end
171

172
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc