Skip to content

Commit 33ab4e7

Browse files
authored
Merge pull request #205 from JuliaDynamics/hw/unused_p
keep track of unused parameters
2 parents d21856a + 3e8a052 commit 33ab4e7

File tree

7 files changed

+72
-29
lines changed

7 files changed

+72
-29
lines changed

.github/workflows/benchmark.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,10 @@ jobs:
2727
"https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}")
2828
PR_TITLE=$(echo "$PR_DATA" | jq -r '.title')
2929
PR_BODY=$(echo "$PR_DATA" | jq -r '.body')
30+
# Ensure PR_BODY is single-line and sanitized for GitHub env vars
31+
PR_BODY_ESCAPED=$(echo "$PR_BODY" | tr '\n' ' ' | tr '\r' ' ')
3032
echo "PR_TITLE=$PR_TITLE" >> $GITHUB_ENV
31-
echo "PR_BODY=$PR_BODY" >> $GITHUB_ENV
33+
echo "PR_BODY=$PR_BODY_ESCAPED" >> $GITHUB_ENV
3234
- name: Check for '[no benchmark]'
3335
id: check
3436
run: |

ext/MTKExt.jl

Lines changed: 37 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,14 @@ function VertexModel(sys::ODESystem, inputs, outputs; verbose=false, name=getnam
5555
sym = [s => _get_metadata(sys, s) for s in _sym]
5656

5757
_psym = getname.(gen.params)
58-
psym = [s => _get_metadata(sys, s) for s in _psym]
58+
psym = map(gen.params) do p
59+
pname = getname(p)
60+
md = _get_metadata(sys, pname)
61+
if p in gen.unused_params
62+
md = (; md..., unused=true)
63+
end
64+
pname => md
65+
end
5966

6067
_obssym = getname.(gen.obsstates)
6168
obssym = [s => _get_metadata(sys, s) for s in _obssym]
@@ -152,7 +159,14 @@ function EdgeModel(sys::ODESystem, srcin, dstin, srcout, dstout; verbose=false,
152159
sym = [s => _get_metadata(sys, s) for s in _sym]
153160

154161
_psym = getname.(gen.params)
155-
psym = [s => _get_metadata(sys, s) for s in _psym]
162+
psym = map(gen.params) do p
163+
pname = getname(p)
164+
md = _get_metadata(sys, pname)
165+
if p in gen.unused_params
166+
md = (; md..., unused=true)
167+
end
168+
pname => md
169+
end
156170

157171
_obssym = getname.(gen.obsstates)
158172
obssym = [s => _get_metadata(sys, s) for s in _obssym]
@@ -373,12 +387,11 @@ function generate_io_function(_sys, inputss::Tuple, outputss::Tuple;
373387
fftype = _determine_fftype(out_deps, states, allinputs, params, iv)
374388

375389
# filter out unnecessary parameters
376-
# var_deps = _all_rhs_symbols(eqs)
377-
# used_params = params ∩ (var_deps ∪ obs_deps ∪ out_deps)
378-
# if Set(params) != Set(used_params)
379-
# verbose && @info "Remove parameters $(collect(setdiff(params, used_params))) which arn't used in the equations."
380-
# params = used_params
381-
# end
390+
var_deps = _all_rhs_symbols(eqs)
391+
unused_params = Set(setdiff(params, (var_deps out_deps))) # do not exclud obs_deps
392+
if verbose && !isempth(unused_params)
393+
@info "Parameters $(unused_params) do not appear in equations of f and g and will be marked as unused."
394+
end
382395

383396
# TODO: explore Symbolcs/SymbolicUtils CSE
384397
# now generate the actual functions
@@ -412,19 +425,22 @@ function generate_io_function(_sys, inputss::Tuple, outputss::Tuple;
412425
obsformulas = [eq.rhs for eq in obseqs]
413426
_, obsf_ip = build_function(obsformulas, states, inputss..., params, iv; cse=true, expression)
414427

415-
return (;f=f_ip, g=g_ip,
416-
mass_matrix,
417-
states,
418-
inputss,
419-
outputss,
420-
obsstates,
421-
fftype,
422-
obsf = obsf_ip,
423-
equations=formulas,
424-
outputeqs=Dict(Iterators.flatten(outputss) .=> gformulas),
425-
observed=Dict(getname.(obsstates) .=> obsformulas),
426-
odesystem=sys,
427-
params)
428+
return (;
429+
f=f_ip, g=g_ip,
430+
mass_matrix,
431+
states,
432+
inputss,
433+
outputss,
434+
obsstates,
435+
fftype,
436+
obsf = obsf_ip,
437+
equations=formulas,
438+
outputeqs=Dict(Iterators.flatten(outputss) .=> gformulas),
439+
observed=Dict(getname.(obsstates) .=> obsformulas),
440+
odesystem=sys,
441+
params,
442+
unused_params
443+
)
428444
end
429445

430446
function _determine_fftype(deps, states, allinputs, params, t)

src/callbacks.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ Creates a callback condition for a [`ComponentCallback`].
3737
Consider a component model with states `[:u1, :u2]`, inputs `[:i]`, outputs
3838
`[:o]` and parameters `[:p1, :p2]`.
3939
40-
ComponentCondition([:u1, :o], [:p1]) do (u, p, t)
40+
ComponentCondition([:u1, :o], [:p1]) do u, p, t
4141
# access states symbolicially or via int index
4242
u[:u1] == u[1]
4343
u[:o] == u[2]
@@ -81,7 +81,7 @@ Creates a callback condition for a [`ComponentCallback`].
8181
Consider a component model with states `[:u1, :u2]`, inputs `[:i]`, outputs
8282
`[:o]` and parameters `[:p1, :p2]`.
8383
84-
ComponentAffect([:u1, :o], [:p1]) do (u, p, ctx)
84+
ComponentAffect([:u1, :o], [:p1]) do u, p, ctx
8585
u[:u1] = 0 # change the state
8686
p[:p1] = 1 # change the parameter
8787
@info "Changed :u1 and :p1 on vertex \$(ctx.vidx)" # access context

src/initialization.jl

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ function initialization_problem(cf::T; t=NaN, apply_bound_transformation, verbos
6060
infree_ms = Tuple((!).(map(s -> has_default(cf, s), sv)) for sv in insym_normalized(cf))
6161
infixs = Tuple(Float64[has_default(cf, s) ? get_default(cf, s) : NaN for s in sv] for sv in insym_normalized(cf))
6262

63-
pfree_m = (!).(map(Base.Fix1(has_default, cf), psym(cf)))
63+
is_free_p = s -> !is_unused(cf, s) && !has_default(cf, s) # free p are not unused and have no default
64+
pfree_m = map(is_free_p, psym(cf))
6465
pfix = Float64[has_default(cf, s) ? get_default(cf, s) : NaN for s in psym(cf)]
6566

6667
# count free variables and equations
@@ -74,7 +75,7 @@ function initialization_problem(cf::T; t=NaN, apply_bound_transformation, verbos
7475
psym(cf)[pfree_m])
7576
@assert length(freesym) == Nfree
7677
if Neqs < Nfree
77-
throw(ArgumentError("Initialization problem underconstraint. $(Neqs) Equations and $(Nfree) variables."))
78+
throw(ArgumentError("Initialization problem underconstraint. $(Neqs) Equations and $(Nfree) variables: $freesym"))
7879
end
7980

8081
# which parts of the nonlinear u (unl) correspond to which free parameters
@@ -282,7 +283,7 @@ function initialize_component!(cf; verbose=true, apply_bound_transformation=true
282283
end
283284

284285
function isinitialized(cf::ComponentModel)
285-
all(has_default_or_init(cf, s) for s in vcat(sym(cf), psym(cf)))
286+
all(has_default_or_init(cf, s) || is_unused(cf, s) for s in vcat(sym(cf), psym(cf)))
286287
end
287288

288289
"""
@@ -304,7 +305,7 @@ function init_residual(cf::T; t=NaN, recalc=false) where {T<:ComponentModel}
304305
outs = Tuple(Float64[get_default_or_init(cf, s) for s in sv] for sv in outsym_normalized(cf))
305306
u = Float64[get_default_or_init(cf, s) for s in sym(cf)]
306307
ins = Tuple(Float64[get_default_or_init(cf, s) for s in sv] for sv in insym_normalized(cf))
307-
p = Float64[get_default_or_init(cf, s) for s in psym(cf)]
308+
p = Float64[is_unused(cf, s) ? NaN : get_default_or_init(cf, s) for s in psym(cf)]
308309
res = zeros(dim(cf) + sum(outdim_normalized(cf)))
309310

310311
res_fg = @views res[1:dim(cf)]

src/metadata.jl

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,18 @@ for md in [:default, :guess, :init, :bounds]
7272
end
7373
set_graphelement!(c::EdgeModel, p::Pair) = set_graphelement!(c, (;src=p.first, dst=p.second))
7474

75+
"""
76+
is_unused(c::ComponentModel, sym::Symbol)
77+
78+
Checks if symbol `sym` is marked as unused (i.e. it does not appear in the equations of f and g explicitly).
79+
"""
80+
function is_unused(c::ComponentModel, sym::Symbol)
81+
if has_metadata(c, sym, :unused)
82+
get_metadata(c, sym, :unused)
83+
else
84+
false
85+
end
86+
end
7587

7688
#### default or init
7789
"""
@@ -318,7 +330,13 @@ end
318330
function _append_states!(lns, cf, syms; sigdigits)
319331
fidx = length(lns)+1
320332
for sym in syms
321-
str = " &" * string(sym) * " &&= "
333+
str = " &"
334+
if is_unused(cf, sym)
335+
str *= styled"{gray:$(string(sym)) (unused)}"
336+
else
337+
str *= string(sym)
338+
end
339+
str *= " &&= "
322340
if has_default_or_init(cf, sym)
323341
val = get_default_or_init(cf, sym)
324342
val_str = str_significant(val; sigdigits, phantom_minus=true)

test/initialization_test.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ end
4848
V=sqrt(u_r^2 + u_i^2), [description="Voltage magnitude"]
4949
ω_ref=0, [description="Reference frequency"]
5050
Pm, [guess=0.1,description="Mechanical Power"]
51+
aux, [description="Auxiliary, unused parameter"]
5152
end
5253
@equations begin
5354
Dt(θ) ~ ω - ω_ref
@@ -66,6 +67,8 @@ end
6667
@test vf.symmetadata[][:bounds] == [-π, π]
6768
@test vf.symmetadata[:i_r][:default] == 1
6869
@test vf.symmetadata[:i_i][:default] == 0.1
70+
@test filter(p->NetworkDynamics.is_unused(vf, p), psym(vf)) == [:aux]
71+
@test NetworkDynamics.has_default_or_init(vf, :aux) == false
6972

7073
NetworkDynamics.initialize_component!(vf; verbose=true)
7174
@test NetworkDynamics.init_residual(vf) < 1e-8
@@ -77,6 +80,7 @@ end
7780
set_default!(vf, , get_init(vf, ))
7881

7982
NetworkDynamics.initialize_component!(vf; verbose=true)
83+
dump_initial_state(vf)
8084
end
8185

8286
@testset "test component initialization with bounds" begin

test/runtests.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ using ExplicitImports
1313

1414
(isinteractive() ? includet : include)(joinpath(pkgdir(NetworkDynamics, "test", "testutils.jl")))
1515

16+
haskey(ENV, "BUILDKITE") && @test CUDA.functional() # fail early in buildkite if cuda is not available
17+
1618
@testset "NetworkDynamics Tests" begin
1719
@testset "Package Quality Tests" begin
1820
# print_explicit_imports(NetworkDynamics)

0 commit comments

Comments
 (0)