Skip to content

Commit 2c8255e

Browse files
feat: add ParameterTimeseriesCollection
1 parent 9a8d1c0 commit 2c8255e

File tree

2 files changed

+208
-0
lines changed

2 files changed

+208
-0
lines changed

src/SymbolicIndexingInterface.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ export parameter_values, set_parameter!, finalize_parameters_hook!,
2727
state_values, set_state!, current_time
2828
include("value_provider_interface.jl")
2929

30+
export ParameterTimeseriesCollection
31+
include("parameter_timeseries_collection.jl")
32+
3033
export getp, setp
3134
include("parameter_indexing.jl")
3235

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
"""
2+
struct ParameterTimeseriesCollection{T}
3+
function ParameterTimeseriesCollection(collection)
4+
5+
A utility struct that helps in storing multiple parameter timeseries. It expects a
6+
collection of timseries objects ([`is_timeseries`](@ref) returns [`Timeseries`](@ref))
7+
for each. Each of the timeseries objects should implement [`state_values`](@ref) and
8+
[`current_time`](@ref). Effectively, the "states" of each contained timeseries object are
9+
the parameter values it stores the timeseries of.
10+
11+
The collection is expected to implement `Base.eachindex`, `Base.iterate` and
12+
`Base.getindex`. The indexes of the collection should agree with the timeseries indexes
13+
returned by calling [`timeseries_parameter_index`](@ref) on the corresponding index
14+
provider.
15+
16+
This type forwards `eachindex`, `iterate` and `length` to the contained `collection`. It
17+
implements `Base.parent` to allow access to the contained `collection`, and has the
18+
following `getindex` methods:
19+
20+
- `getindex(ptc::ParameterTimeseriesCollection, idx) = ptc.collection[idx]`.
21+
- `getindex(::ParameterTimeseriesCollection, idx::ParameterTimeseriesIndex)` returns the
22+
timeseries of the parameter referred to by `idx`.
23+
- `getindex(::ParameterTimeseriesCollection, idx::ParameterTimeseriesIndex, subidx)`
24+
returns the value of the parameter referred to by `idx` at the time index `subidx`.
25+
- Apart from these cases, if multiple indexes are provided the first is treated as a
26+
timeseries index, the second the time index in the timeseries, and the (optional)
27+
third the index of the parameter in an element of the timeseries.
28+
29+
The three-argument version of [`parameter_values`](@ref) is implemented for this type.
30+
The single-argument version of `parameter_values` returns the cached parameter object.
31+
This type does not implement any traits.
32+
"""
33+
struct ParameterTimeseriesCollection{T, P}
34+
collection::T
35+
paramcache::P
36+
37+
function ParameterTimeseriesCollection(collection::T, paramcache::P) where {T, P}
38+
if any(x -> is_timeseries(x) == NotTimeseries(), collection)
39+
throw(ArgumentError("""
40+
All objects in the collection `ParameterTimeseriesCollection` must be \
41+
timeseries objects.
42+
"""))
43+
end
44+
new{T, P}(collection, paramcache)
45+
end
46+
end
47+
48+
Base.eachindex(ptc::ParameterTimeseriesCollection) = eachindex(ptc.collection)
49+
50+
Base.iterate(ptc::ParameterTimeseriesCollection, args...) = iterate(ptc.collection, args...)
51+
52+
Base.length(ptc::ParameterTimeseriesCollection) = length(ptc.collection)
53+
54+
Base.parent(ptc::ParameterTimeseriesCollection) = ptc.collection
55+
56+
Base.getindex(ptc::ParameterTimeseriesCollection, idx) = ptc.collection[idx]
57+
function Base.getindex(ptc::ParameterTimeseriesCollection, idx::ParameterTimeseriesIndex)
58+
timeseries = ptc.collection[idx.timeseries_idx]
59+
return getindex.(state_values(timeseries), (idx.parameter_idx,))
60+
end
61+
function Base.getindex(
62+
ptc::ParameterTimeseriesCollection, idx::ParameterTimeseriesIndex, subidx::Union{
63+
Int, CartesianIndex})
64+
timeseries = ptc.collection[idx.timeseries_idx]
65+
return state_values(timeseries, subidx)[idx.parameter_idx]
66+
end
67+
function Base.getindex(
68+
ptc::ParameterTimeseriesCollection, idx::ParameterTimeseriesIndex, ::Colon)
69+
return ptc[idx]
70+
end
71+
function Base.getindex(
72+
ptc::ParameterTimeseriesCollection, idx::ParameterTimeseriesIndex, subidx::AbstractArray{Bool})
73+
timeseries = ptc.collection[idx.timeseries_idx]
74+
map(only(to_indices(current_time(timeseries), (subidx,)))) do i
75+
state_values(timeseries, i)[idx.parameter_idx]
76+
end
77+
end
78+
function Base.getindex(
79+
ptc::ParameterTimeseriesCollection, idx::ParameterTimeseriesIndex, subidx)
80+
timeseries = ptc.collection[idx.timeseries_idx]
81+
getindex.(state_values.((timeseries,), subidx), idx.parameter_idx)
82+
end
83+
function Base.getindex(ptc::ParameterTimeseriesCollection, ts_idx, subidx)
84+
return state_values(ptc.collection[ts_idx], subidx)
85+
end
86+
function Base.getindex(ptc::ParameterTimeseriesCollection, ts_idx, subidx, param_idx)
87+
return ptc[ParameterTimeseriesIndex(ts_idx, param_idx), subidx]
88+
end
89+
90+
function parameter_values(ptc::ParameterTimeseriesCollection)
91+
return ptc.paramcache
92+
end
93+
94+
function parameter_values(
95+
ptc::ParameterTimeseriesCollection, idx::ParameterTimeseriesIndex, subidx)
96+
return ptc[idx, subidx]
97+
end
98+
function parameter_values(prob, i::ParameterTimeseriesIndex, j)
99+
parameter_values(get_parameter_timeseries_collection(prob), i, j)
100+
end
101+
function parameter_timeseries(ptc::ParameterTimeseriesCollection, idx)
102+
return current_time(ptc[idx])
103+
end
104+
105+
function _timeseries_value(ptc::ParameterTimeseriesCollection, ts_idx, t)
106+
ts_obj = ptc[ts_idx]
107+
time_idx = searchsortedlast(current_time(ts_obj), t)
108+
value = state_values(ts_obj, time_idx)
109+
return value
110+
end
111+
112+
"""
113+
parameter_values_at_time(valp, t)
114+
115+
Return an indexable collection containing the value of all parameters in `valp` at time
116+
`t`. Note that `t` here is a floating-point time, and not an index into a timeseries.
117+
118+
This has a default implementation relying on [`get_parameter_timeseries_collection`](@ref)
119+
and [`with_updated_parameter_timeseries_values`](@ref).
120+
"""
121+
function parameter_values_at_time(valp, t)
122+
ptc = get_parameter_timeseries_collection(valp)
123+
with_updated_parameter_timeseries_values(ptc.paramcache,
124+
(ts_idx => _timeseries_value(ptc, ts_idx, t) for ts_idx in eachindex(ptc))...)
125+
end
126+
127+
"""
128+
parameter_values_at_state_time(valp, i)
129+
parameter_values_at_state_time(valp)
130+
131+
Return an indexable collection containing the value of all parameters in `valp` at time
132+
index `i` in the state timeseries.
133+
134+
By default, this function relies on [`parameter_values_at_time`](@ref) and
135+
[`current_time`](@ref) for a default implementation.
136+
137+
The single-argument version of this function is a shorthand to return parameter values
138+
at each point in the state timeseries. This also has a default implementation relying on
139+
[`parameter_values_at_time`](@ref) and [`current_time`](@ref).
140+
"""
141+
function parameter_values_at_state_time end
142+
143+
function parameter_values_at_state_time(p, i)
144+
state_time = current_time(p, i)
145+
return parameter_values_at_time(p, state_time)
146+
end
147+
function parameter_values_at_state_time(p)
148+
return (parameter_values_at_time(p, t) for t in current_time(p))
149+
end
150+
151+
"""
152+
parameter_timeseries(valp, i)
153+
154+
Return a vector of the time steps at which the parameter values in the parameter
155+
timeseries at index `i` are saved. This is only required for objects where
156+
`is_parameter_timeseries(valp) === Timeseries()`. It will not be called otherwise. It is
157+
assumed that the timeseries is sorted in increasing order.
158+
159+
See also: [`is_parameter_timeseries`](@ref).
160+
"""
161+
function parameter_timeseries end
162+
163+
function parameter_timeseries(valp, i)
164+
return parameter_timeseries(get_parameter_timeseries_collection(valp), i)
165+
end
166+
167+
"""
168+
parameter_timeseries_at_state_time(valp, i, j)
169+
parameter_timeseries_at_state_time(valp, i)
170+
171+
Return the index of the timestep in the parameter timeseries at timeseries index `i` which
172+
occurs just before or at the same time as the state timestep with index `j`. The two-
173+
argument version of this function returns an iterable of indexes, one for each timestep in
174+
the state timeseries. If `j` is an object that refers to multiple values in the state
175+
timeseries (e.g. `Colon`), return an iterable of the indexes in the parameter timeseries
176+
at the appropriate points.
177+
178+
Both versions of this function have default implementations relying on
179+
[`current_time`](@ref) and [`parameter_timeseries`](@ref), for the cases where `j` is one
180+
of: `Int`, `CartesianIndex`, `AbstractArray{Bool}`, `Colon` or an iterable of the
181+
aforementioned.
182+
"""
183+
function parameter_timeseries_at_state_time end
184+
185+
function parameter_timeseries_at_state_time(valp, i, j::Union{Int, CartesianIndex})
186+
state_time = current_time(valp, j)
187+
timeseries = parameter_timeseries(valp, i)
188+
searchsortedlast(timeseries, state_time)
189+
end
190+
191+
function parameter_timeseries_at_state_time(valp, i, ::Colon)
192+
parameter_timeseries_at_state_time(valp, i)
193+
end
194+
195+
function parameter_timeseries_at_state_time(valp, i, j::AbstractArray{Bool})
196+
parameter_timeseries_at_state_time(valp, i, only(to_indices(current_time(valp), (j,))))
197+
end
198+
199+
function parameter_timeseries_at_state_time(valp, i, j)
200+
(parameter_timeseries_at_state_time(valp, i, jj) for jj in j)
201+
end
202+
203+
function parameter_timeseries_at_state_time(valp, i)
204+
parameter_timeseries_at_state_time(valp, i, eachindex(current_time(valp)))
205+
end

0 commit comments

Comments
 (0)