Skip to content
Merged
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
63 changes: 63 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
## GraphDynamics v0.7.0

### New features

+ A `PolyesterScheduler()` scheduling object has been added which allows for parallelizing solves using Polyester.jl. This option helps performance during ODE solves where GC time dominates if multithreaded.
+ If any subsystem in a problem has a parameter named `dtmax`, then the smallest `dtmax` parameter in the system is forwarded as a keyword argument to `ODEProblem` and `SDEProblem`s to limit the maximum stepsizes allowed by the solver.
+ If a connection type overrides `GraphDynamics.connection_needs_ctx` to give true, then connections of that type will be given a fourth `ctx` argument which gives it access to the full list of `states_partitioned`, `params_partitioned`, and `connection_matrices` when accumulating inputs.
+ GraphDynamics integrates with Latexify.jl to latexify the equations of a GraphSystem.

### Breaking changes

+ `PartitionedGraphSystem` has been removed; `GraphSystem`s holds a field `g.flat_graph` field with a `PartitioningGraphSystem` object which actively flattens and partitions the graph during solving
+ `apply_discrete_event!`, `apply_continuous_event!`, and `ForeachConnectedSubsystem` have had their `vstates` and `vparams` arguments combined into a `sys_view` argument, which gives a view into the affected system for (and the connection form gets a `sys_view_src` and `sys_view_dst`). This `sys_view` can have it's fields be updated in place like so:
```julia
function GraphDynamics.apply_discrete_event!(integrator, sys_view, sys::Subsystem{MyType}, _)
sys_view.x[] = sys.y
end
```
This will modify the `x` state or parameter of the system when the event triggers.

+ `apply_subsystem_noise!`'s first argument is now modified in the same way as the view arguments of `apply_discrete_event!`. One would now write e.g.
```julia
function GraphDynamics.apply_subsystem_noise!(vstate, sys::Subsystem{BrownianParticle}, t)
# No noise in position, so we don't modify vstate[:x]
vstate.v[] = sys.σ # White noise in velocity with amplitude σ
end
```
rather than `vstate[:v] = sys.σ`

## GraphDynamics v0.6.0

[Diff since v0.5.0](https://github.com/Neuroblox/GraphDynamics.jl/compare/v0.5.0...v0.6.0)

### Breaking changes

Switched `computed_properties` and `computed_properties_with_inputs` to take a type tag instead of subsystem argument. Now, instead of adding methods like
```julia
function GraphDynamics.computed_properties_with_inputs(::Subsystem{Particle})
a(sys, input) = input.F / sys.m
(; a)
end
```
users should do
```julia
function GraphDynamics.computed_properties_with_inputs(::Type{Particle})
a(sys, input) = input.F / sys.m
(; a)
end
```

## GraphDynamics v0.5.0

[Diff since v0.4.9](https://github.com/Neuroblox/GraphDynamics.jl/compare/v0.4.9...v0.5.0)

### Breaking changes

- Removed the fallback `(cr::ConnectionRule)(src, dst, t) = cr(src, dst)` which was added previously to avoid breakage when we made connection rules take a time argument in addition to the `src` and `dst` subsystems. The presence of this method was a bit of a annoying crutch
- Changed the arguments of `discrete_event_condition` from just `(conn, t)` to `(conn, t, sys_src, sys_dst)` so that events can trigger based off information in the source or destination subsystems as well
- Changed the arguments of `has_discrete_events` from just `(typeof(conn),)` for connection events to `(typeof(conn), get_tag(src), get_tag(dst))`, i.e. it now also can depend on the types of the source and dest subsystems
- Changed the arguments of `event_times` from just `(conn,)` to `(conn, sys_src, sys_dst)` for connection events.

**Merged pull requests:**
- Remove `(::ConnectionRule)(src, dst, t)` fallback method; make `discrete_event_condition` on connections take `src` / `dst` args. (#44) (@MasonProtter)
14 changes: 9 additions & 5 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name = "GraphDynamics"
uuid = "bcd5d0fe-e6b7-4ef1-9848-780c183c7f4c"
version = "0.6.0"
version = "0.7.0"

[workspace]
projects = ["test", "scrap"]
Expand All @@ -9,32 +9,36 @@ projects = ["test", "scrap"]
Accessors = "7d9f7c33-5ae7-4f3b-8dc6-eff91059b697"
ConstructionBase = "187b0558-2788-49d3-abe0-74a17ed4e7c9"
DiffEqBase = "2b5f629d-d688-5b77-993f-72d75c75574e"
FieldViews = "ff5a1669-b1f2-423e-bbd7-b7fa0f7e0224"
OhMyThreads = "67456a42-1dca-4109-a031-0a68de7e3ad5"
OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
RecursiveArrayTools = "731186ca-8d62-57ce-b412-fbd966d074cd"
SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462"
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
SymbolicIndexingInterface = "2efcf032-c050-4f8e-a9bb-153293bab1f5"
Polyester = "f517fe37-dbe3-4b94-8317-1923a5111588"

[weakdeps]
ModelingToolkit = "961ee093-0014-501f-94e3-6117800e7a78"
Latexify = "23fbe1c1-3f47-55db-b15f-69d7ec21a316"
Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7"

[extensions]
MTKExt = ["Symbolics", "ModelingToolkit"]
LatexifyExt = ["Symbolics", "Latexify"]
SymbolicsExt = ["Symbolics"]

[compat]
Accessors = "0.1"
ConstructionBase = "1.5"
DiffEqBase = "6"
ModelingToolkit = "9, 10"
FieldViews = "0.3.1"
Latexify = "0.16.10"
OhMyThreads = "0.6, 0.7, 0.8"
OrderedCollections = "1.6.3"
RecursiveArrayTools = "3"
SciMLBase = "2"
SparseArrays = "1"
SymbolicIndexingInterface = "0.3"
Symbolics = "6"
Symbolics = "6, 7"
julia = "1.10"

[extras]
Expand Down
152 changes: 152 additions & 0 deletions ext/LatexifyExt.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
module LatexifyExt

using Symbolics

using Latexify

using GraphDynamics:
GraphDynamics,
get_tag,
get_name,
initialize_input,
subsystem_differential,
get_states,
get_params,
to_subsystem,
Subsystem,
SubsystemStates,
SubsystemParams,
ConnectionRule,
connection_property_namemap,
GraphSystem,
nodes,
connections,
graph_equations,
node_equations,
connection_equations,
ConnectionIndex,
GraphSystemConnection,
system_wiring_rule!,
PartitioningGraphSystem

@variables t
const _D = Differential(t)
const NAMESPACE_SEPARATOR_SYMBOL = Symbol(Symbolics.NAMESPACE_SEPARATOR)

function GraphDynamics.node_equations(sys::T) where T
sym_subsys, state_vars, input_vars = to_symbolic_subsystem(sys)
rhss = subsystem_differential(sym_subsys, input_vars, t)
eqs = [_D(state_vars[i]) ~ rhss[i] for i in 1:length(rhss)]
end

function GraphDynamics.node_equations(list_of_sys::Union{Tuple, Vector, Set, Base.KeySet})
equations = map(collect(list_of_sys)) do n
node_equations(n)
end
reduce(vcat, equations)
end

function GraphDynamics.connection_equations(sys::GraphSystem, src, dst)
subgraph = GraphSystem()
for (; data) in connections(sys, src, dst)
add_connection!(subgraph, src, dst; data...)
end
subnodes = [n for n ∈ nodes(subgraph) if n ∉ (src, dst)]
[connection_equations(subgraph); node_equations(subnodes)]
end

function GraphDynamics.connection_equations(sys::PartitioningGraphSystem)
system_cache = Dict{Any, Tuple{Subsystem, NamedTuple, NamedTuple}}()
eqs = map(connections(sys)) do (; src, dst, conn)
GraphDynamics.connection_equations(conn, src, dst; system_cache)
end
reduce(vcat, eqs)
end

function GraphDynamics.connection_equations(sys::GraphSystem)
GraphDynamics.connection_equations(sys.flat_graph)
end

function GraphDynamics.connection_equations(conn::ConnectionRule, src, dst; system_cache=nothing)
if isnothing(system_cache)
sym_srcsys, _, _ = to_symbolic_subsystem(src)
sym_dstsys, inputs, _ = to_symbolic_subsystem(dst)
else
sym_srcsys, _, _ = get!(system_cache, src) do
to_symbolic_subsystem(src)
end
sym_dstsys, inputs, _ = get!(system_cache, dst) do
to_symbolic_subsystem(dst)
end
end
conn_props = connection_property_namemap(conn, get_name(src), get_name(dst))
syms = map(collect(pairs(conn_props))) do (k, v)
if getfield(conn, k) isa Function
only(@variables $v(..))
else
only(@variables $v)
end
end

cons = typeof(conn).name.wrapper
sym_conn = cons(syms...)
rhss = sym_conn(sym_srcsys, sym_dstsys, t)
lhss = gen_variables(Val(keys(rhss)), Val(get_name(dst)), Val(true))

eqs = map(zip(lhss, rhss)) do (lhs, rhs)
lhs ~ rhs
end
end

function namespaced_vars(namespace, syms; of_t = true)
ns_syms = map(syms) do name
Symbol(namespace, NAMESPACE_SEPARATOR_SYMBOL, name)
end

if of_t
Tuple([Expr(:call, sym, :t) for sym in ns_syms])
else
Tuple(ns_syms)
end
end

function to_symbolic_subsystem(sys)
to_symbolic_subsystem(to_subsystem(sys); namespace = get_name(sys))
end

@generated function gen_variables(::Val{syms}, ::Val{namespace}, ::Val{of_t}) where {syms, namespace, of_t}
nsyms = namespaced_vars(namespace, syms; of_t)
quote
vars = @variables $(Expr(:tuple, nsyms...))
NamedTuple{$(syms)}(vars)
end
end

function to_symbolic_subsystem(sys::Subsystem{T}; namespace = :sys) where T
states = propertynames(get_states(sys))
params = propertynames(get_params(sys))
inputs = propertynames(initialize_input(sys))

ns = Val(namespace)
state_vars = gen_variables(Val(states), ns, Val(true))
input_vars = gen_variables(Val(inputs), ns, Val(true))
param_vars = gen_variables(Val(params), ns, Val(false))

sym_subsys_states = SubsystemStates{get_tag(sys)}(NamedTuple{states}(state_vars))
sym_subsys_params = SubsystemParams{get_tag(sys)}(NamedTuple{params}(param_vars))
sym_subsys = Subsystem{get_tag(sys)}(sym_subsys_states, sym_subsys_params)

sym_subsys, state_vars, input_vars
end

@latexrecipe function f(sys::Union{GraphSystem, PartitioningGraphSystem}; show_connection_equations = true)
sys = sys isa GraphSystem ? sys.flat_graph : sys
if show_connection_equations
return latexify([node_equations(collect(nodes(sys)));
connection_equations(sys)])
else
return latexify(node_equations(collect(nodes(sys))))
end
end

end
40 changes: 0 additions & 40 deletions ext/MTKExt.jl

This file was deleted.

39 changes: 39 additions & 0 deletions ext/SymbolicsExt.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
module SymbolicsExt

using Symbolics: Symbolics, tosymbol, Num
using GraphDynamics: GraphDynamics, GraphSystemParameters, GraphNamemap
using SymbolicIndexingInterface: SymbolicIndexingInterface

function SymbolicIndexingInterface.is_variable(sys::GraphNamemap, var::Num)
SymbolicIndexingInterface.is_variable(sys, tosymbol(var; escape=false))
end

function SymbolicIndexingInterface.variable_index(sys::GraphNamemap, var::Num)
SymbolicIndexingInterface.variable_index(sys, tosymbol(var; escape=false))
end

function SymbolicIndexingInterface.is_parameter(sys::GraphNamemap, var::Num)
SymbolicIndexingInterface.is_parameter(sys, tosymbol(var; escape=false))
end

function SymbolicIndexingInterface.parameter_index(sys::GraphNamemap, var::Num)
SymbolicIndexingInterface.parameter_index(sys, tosymbol(var; escape=false))
end

function SymbolicIndexingInterface.is_independent_variable(sys::GraphNamemap, var::Num)
SymbolicIndexingInterface.is_independent_variable(sys, tosymbol(var; escape=false))
end

function SymbolicIndexingInterface.is_observed(sys::GraphNamemap, var::Num)
SymbolicIndexingInterface.is_observed(sys, tosymbol(var; escape=false))
end

function SymbolicIndexingInterface.observed(sys::GraphNamemap, var::Num)
SymbolicIndexingInterface.observed(sys, tosymbol(var; escape=false))
end

function SymbolicIndexingInterface.observed(sys::GraphNamemap, vars::Union{Vector{Num}, Tuple{Vararg{Num}}})
SymbolicIndexingInterface.observed(sys, tosymbol.(vars; escape=false))
end

end
Loading