Skip to content

yaml imput #232

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,20 @@ SteadyStateDiffEq = "9672c7b4-1e72-59bd-8a11-6ac3964bc41f"
StyledStrings = "f489334b-da3d-4c2e-b8f0-e476e12c162b"
SymbolicIndexingInterface = "2efcf032-c050-4f8e-a9bb-153293bab1f5"
TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f"
YAML = "ddb6d928-2868-570f-bddf-ab3f9cf99eb6"

[weakdeps]
Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e"
CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba"
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09"
ModelingToolkit = "961ee093-0014-501f-94e3-6117800e7a78"
SymbolicUtils = "d1185830-fcd6-423d-90d6-eec64667417b"
Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7"

[extensions]
NetworkDynamicsCUDAExt = ["CUDA", "Adapt"]
NetworkDynamicsDataFramesExt = ["DataFrames"]
NetworkDynamicsMTKExt = ["ModelingToolkit", "SymbolicUtils", "Symbolics"]
NetworkDynamicsSymbolicsExt = ["Symbolics", "MacroTools"]

Expand Down Expand Up @@ -81,4 +84,5 @@ SymbolicIndexingInterface = "0.3.27"
SymbolicUtils = "3.24"
Symbolics = "6.19.0"
TimerOutputs = "0.5.23"
YAML = "0.4.12"
julia = "1.10"
2 changes: 1 addition & 1 deletion docs/src/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ EdgeModel()
```@docs
VertexModel(::ModelingToolkit.ODESystem, ::Any, ::Any)
EdgeModel(::ModelingToolkit.ODESystem, ::Any, ::Any, ::Any, ::Any)
EdgeModel(::ModelingToolkit.ODESystem, ::Any, ::Any, ::Any)
EdgeModel(::ModelingToolkit.ODESystem, ::Any, ::Any, ::NetworkDynamics.AnnotatedSym)
```

### Output Function Helpers/Wrappers
Expand Down
96 changes: 96 additions & 0 deletions ext/NetworkDynamicsDataFramesExt.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
module NetworkDynamicsDataFramesExt

using NetworkDynamics: NetworkDynamics, Network
using DataFrames: DataFrames, DataFrame
using OrderedCollections: OrderedDict

function _df_from_list(list, f)
df = DataFrame()
for (i, v) in list
row = OrderedDict{Symbol,Any}()
row[:idx] = i
for sym in f(v)
if NetworkDynamics.has_default_or_init(v, sym)
row[sym] = NetworkDynamics.get_default_or_init(v, sym)
else
row[sym] = missing
end
end
push!(df, NamedTuple(row); cols=:union)
end
df
end
function _df_from_pair(list, pair)
key, f = pair
df = DataFrame()
for (i, v) in list
row = OrderedDict{Symbol,Any}(:idx => i, key => f(v))
push!(df, NamedTuple(row); cols=:union)
end
df
end

function NetworkDynamics.describe_vertices(nw::Network, extras...; parameters=true, states=true, batch=nothing)
pairs = enumerate(nw.im.vertexm);
batches = map(idx -> findfirst(batch -> idx ∈ batch.indices, nw.vertexbatches), first.(pairs))

if !isnothing(batch)
idxs = findall(b -> b ∈ batch, batches)
pairs = collect(pairs)[idxs]
batch = collect(batches)[idxs]
end
isempty(pairs) && return DataFrame()

basedf = DataFrame(
idx = first.(pairs),
name = map(v->last(v).name, pairs),
batch = map(idx -> findfirst(batch -> idx ∈ batch.indices, nw.vertexbatches), first.(pairs)),
)

dfs = [basedf,]
if parameters
push!(dfs, _df_from_list(pairs, NetworkDynamics.psym))
end
if states
push!(dfs, _df_from_list(pairs, NetworkDynamics.sym))
end
for p in extras
push!(dfs, _df_from_pair(pairs, p))
end

foldl((a,b) -> DataFrames.leftjoin(a,b; on=:idx), dfs)
end

function NetworkDynamics.describe_edges(nw::Network, extras...; parameters=true, states=true, batch=nothing)
pairs = enumerate(nw.im.edgem);
batches = map(idx -> findfirst(batch -> idx ∈ batch.indices, nw.layer.edgebatches), first.(pairs))

if !isnothing(batch)
idxs = findall(b -> b ∈ batch, batches)
pairs = collect(pairs)[idxs]
batch = collect(batches)[idxs]
end
isempty(pairs) && return DataFrame()

basedf = DataFrame(
idx = first.(pairs),
srcdst = map(idx -> nw.im.edgevec[idx].src => nw.im.edgevec[idx].dst, first.(pairs)),
name = map(v->last(v).name, pairs),
batch = map(idx -> findfirst(batch -> idx ∈ batch.indices, nw.vertexbatches), first.(pairs)),
)

dfs = [basedf,]
if parameters
push!(dfs, _df_from_list(pairs, NetworkDynamics.psym))
end
if states
push!(dfs, _df_from_list(pairs, NetworkDynamics.sym))
end
for p in extras
push!(dfs, _df_from_pair(pairs, p))
end

foldl((a,b) -> DataFrames.leftjoin(a,b; on=:idx), dfs)
end

end
28 changes: 25 additions & 3 deletions ext/NetworkDynamicsMTKExt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,26 @@ the symbol vector in
- `Directed(dstout)`.

Additional `kwargs` are the same as for the double-sided EdgeModel MTK constructor.

Instead of wrapping, you can also provide the annotation as keyword argument like `annotation=:AntiSymmetric`.
"""
EdgeModel(sys::ODESystem, srcin, dstin, dstout; kwargs...) = EdgeModel(sys, srcin, dstin, nothing, dstout; kwargs...)
function EdgeModel(sys::ODESystem, srcin, dstin, dstout::AnnotatedSym; kwargs...)
EdgeModel(sys, srcin, dstin, nothing, dstout; kwargs...)
end
function EdgeModel(sys::ODESystem, srcin, dstin, dstout; annotation=nothing, kwargs...)
_dstout = if annotation==:AntiSymmetric
AntiSymmetric(dstout)
elseif annotation==:Symmetric
Symmetric(dstout)
elseif annotation==:Directed
Directed(dstout)
else
throw(ArgumentError("When constructing single sided EdgeModel, you must either wrap \
the output syms (e.g. AntiSymmetric(dstout)) orprovide the annotation as keyword \
argument."))
end
EdgeModel(sys, srcin, dstin, nothing, _dstout; kwargs...)
end

"""
EdgeModel(sys::ODESystem, srcin, srcout, dstin, dstout;
Expand Down Expand Up @@ -220,8 +238,12 @@ function _get_metadata(sys, name)
if def isa Symbolic
def = fixpoint_sub(def, alldefaults)
end
def isa Symbolic && error("Could not resolve default $(ModelingToolkit.getdefault(sym)) for $name")
nt = (; nt..., default=def)

if def isa Symbolic
@warn "Could not resolve default term $(ModelingToolkit.getdefault(sym)) for $name, leave free."
else
nt = (; nt..., default=def)
end
end

# check for guess both in symbol metadata and in guesses of system
Expand Down
23 changes: 22 additions & 1 deletion src/NetworkDynamics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ using InteractiveUtils: subtypes

import SymbolicIndexingInterface as SII
using StaticArrays: StaticArrays, SVector
using YAML: YAML
using OrderedCollections: OrderedDict

include("utils.jl")

Expand Down Expand Up @@ -96,6 +98,12 @@ const CHECK_COMPONENT = Ref(true)
export chk_component
include("doctor.jl")

export load_network
include("importexport.jl")

export describe_vertices, describe_edges
function describe_vertices end
function describe_edges end
#=
using StyledStrings
s1 = styled"{bright_red:brred} {bright_green:brgreen} {bright_yellow:bryellow} {bright_blue:brblue} {bright_magenta:brmagenta} {bright_cyan:brcyan} {bright_black:brblack} {bright_white:brwhite}";
Expand All @@ -114,7 +122,20 @@ const ND_FACES = [
:NetworkDynamics_fftype => StyledStrings.Face(foreground=:bright_blue),
]

__init__() = foreach(StyledStrings.addface!, ND_FACES)
function __init__()
if isdefined(Base.Experimental, :register_error_hint)
Base.Experimental.register_error_hint(MethodError) do io, exc, argtypes, kwargs
if exc.f ∈ (describe_vertices, describe_edges)
ext = Base.get_extension(NetworkDynamics, :NetworkDynamicsDataFramesExt)
if isnothing(ext)
printstyled(io, "\nLoad `DataFrames` in order to use `describe_vertices` and `describe_edges`."; bold=true)
end
end
end
end

foreach(StyledStrings.addface!, ND_FACES)
end

function reloadfaces!()
StyledStrings.resetfaces!()
Expand Down
12 changes: 6 additions & 6 deletions src/component_functions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ See also [`Symmetric`](@ref), [`Directed`](@ref), [`Fiducial`](@ref) and [`State
struct AntiSymmetric{G} <: SingleSidedOutputWrapper
g::G
end
AntiSymmetric(g::Union{AbstractVector,Number}) = AntiSymmetric(StateMask(g))
AntiSymmetric(g::Union{AbstractVector{<:Number},Number}) = AntiSymmetric(StateMask(g))
@inline function (c::AntiSymmetric)(osrc, odst, args...)
@inline c.g(odst, args...)
@inbounds for i in 1:length(osrc)
Expand All @@ -142,7 +142,7 @@ See also [`AntiSymmetric`](@ref), [`Directed`](@ref), [`Fiducial`](@ref) and [`S
struct Symmetric{G} <: SingleSidedOutputWrapper
g::G
end
Symmetric(g::Union{AbstractVector,Number}) = Symmetric(StateMask(g))
Symmetric(g::Union{AbstractVector{<:Number},Number}) = Symmetric(StateMask(g))
@inline function (c::Symmetric)(osrc, odst, args...)
@inline c.g(odst, args...)
@inbounds for i in 1:length(osrc)
Expand All @@ -167,7 +167,7 @@ See also [`AntiSymmetric`](@ref), [`Symmetric`](@ref), [`Fiducial`](@ref) and [`
struct Directed{G} <: SingleSidedOutputWrapper
g::G
end
Directed(g::Union{AbstractVector,Number}) = Directed(StateMask(g))
Directed(g::Union{AbstractVector{<:Number},Number}) = Directed(StateMask(g))
@inline function (c::Directed)(osrc, odst, args...)
@inline c.g(odst, args...)
nothing
Expand Down Expand Up @@ -226,23 +226,23 @@ Annotate a vector of output-symbols as `AntiSymmetric`, used when creating `Edge
single-sided MTK models.
"""
AntiSymmetric(s::Symbol) = AntiSymmetric([s])
AntiSymmetric(s::AbstractVector{<:Symbol}) = AnnotatedSym(AntiSymmetric, s)
AntiSymmetric(s::AbstractVector) = AnnotatedSym(AntiSymmetric, convert(Vector{Symbol}, s))
"""
Symmetric(s::AbstractVector{<:Symbol})

Annotate a vector of output-symbols as `Symmetric`, used when creating `EdgeModel`s from
single-sided MTK models.
"""
Symmetric(s::Symbol) = Symmetric([s])
Symmetric(s::AbstractVector{<:Symbol}) = AnnotatedSym(Symmetric, s)
Symmetric(s::AbstractVector) = AnnotatedSym(Symmetric, convert(Vector{Symbol}, s))
"""
Directed(s::AbstractVector{<:Symbol})

Annotate a vector of output-symbols as `Directed`, used when creating `EdgeModel`s from
single-sided MTK models.
"""
Directed(s::Symbol) = Directed([s])
Directed(s::AbstractVector{<:Symbol}) = AnnotatedSym(Directed, s)
Directed(s::AbstractVector) = AnnotatedSym(Directed, convert(Vector{Symbol}, s))


abstract type ComponentModel end
Expand Down
10 changes: 9 additions & 1 deletion src/construction.jl
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ function _component_hash(c::ComponentModel)
))
end

function Network(vertexfs, edgefs; kwargs...)
function Network(vertexfs, edgefs; warn_order=true, kwargs...)
vertexfs = vertexfs isa VertexModel ? [vertexfs] : vertexfs
edgefs = edgefs isa EdgeModel ? [edgefs] : edgefs
@argcheck all(has_graphelement, vertexfs) "All vertex models must have assigned `graphelement` to implicitly construct graph!"
Expand Down Expand Up @@ -269,6 +269,14 @@ function Network(vertexfs, edgefs; kwargs...)

vfs_ordered = [vdict[k] for k in vertices(g)]
efs_ordered = [edict[k] for k in edges(g)]
if warn_order && any(vfs_ordered .!== vertexfs)
@warn "Order of vertex models was changed to match the natural ordering of vertices in graph!\
Disable warning with kw `warn_order=false`."
end
if warn_order && any(efs_ordered .!== edgefs)
@warn "Order of edge models was changed to match the natural ordering edges in graph! \
Disable warning with kw `warn_order=false`."
end

Network(g, vfs_ordered, efs_ordered; check_graphelement=false, kwargs...)
end
Expand Down
Loading
Loading