1+
2+
3+ evaluate (e:: DiscreteEvaluation , sys, dt1:: DataFrame , dt2:: DataFrame ) = throw (" Unimplemented evaluate for $(typeof (e)) " )
4+
5+ function _require_solution (sys, dt:: DataFrame ; name= nothing )
6+ @assert " timestamp" ∈ names (dt) " The dataset $name must contain a column named `timestamp`"
7+ measured = measured_values (sys)
8+ measured_cols = measured_names (measured)
9+ @assert all (c-> c ∈ names (dt), measured_cols) " All measured values must exist in both datasets (missing value in $name )"
10+ end
11+
12+ function _require_joint_solution (sys, dt1, dt2; check_only_final = false )
13+ _require_solution (sys, dt1; name = " dt1" )
14+ _require_solution (sys, dt2; name = " dt2" )
15+ if ! check_only_final
16+ @assert dt1[:, :timestamp ] == dt2[:, :timestamp ] " Both data frames need to share a temporal discretization"
17+ else
18+ @assert dt1[end , :timestamp ] == dt2[end , :timestamp ] " Ending times differ"
19+ end
20+ end
21+
22+ struct FinalState{M<: Metric } <: DiscreteEvaluation
23+ name:: Symbol
24+ metric:: M
25+ FinalState (m:: M ; name= :final ) where {M <: Metric } = new {M} (name, m)
26+ end
27+ function evaluate (f:: FinalState , sys, dt1:: DataFrame , dt2:: DataFrame )
28+ _require_joint_solution (sys, dt1, dt2; check_only_final = true )
29+ measured_cols = setdiff (intersect (names (dt1), names (dt2)), " timestamp" )
30+ output = compare (f. metric, sys, dt1[end , measured_cols], dt2[end , measured_cols])
31+ return name (f) => Dict (outputs (f. metric) .=> output)
32+ end
33+ name (f:: FinalState ) = f. name
34+
35+ struct Accumulate{M<: Metric , T} <: DiscreteEvaluation
36+ name:: Symbol
37+ metric:: M
38+ op:: T
39+ Accumulate (m:: M ; name= :stage ) where {M<: Metric } = let op = acc_op (m); new {M, typeof(op)} (name, m, op) end
40+ Accumulate (m:: M , op:: T ; name= :stage ) where {M<: Metric , T} = new {M, T} (name, m, op)
41+ end
42+ name (f:: Accumulate ) = f. name
43+
44+ function evaluate (f:: Accumulate , sys, dt1:: DataFrame , dt2:: DataFrame ; init = nothing )
45+ _require_joint_solution (sys, dt1, dt2)
46+ measured_cols = setdiff (intersect (names (dt1), names (dt2)), " timestamp" )
47+ output = isnothing (init) ? initial_output (f. metric) : init
48+ for (row1, row2) in Iterators. zip (eachrow (dt1[! , measured_cols]), eachrow (dt2[! , measured_cols]))
49+ new_output = compare (f. metric, sys, row1, row2)
50+ output = f. op (output, new_output)
51+ end
52+ return name (f) => Dict (outputs (f. metric) .=> output)
53+ end
54+
55+ struct Mean{M<: Metric , T} <: DiscreteEvaluation
56+ name:: Symbol
57+ metric:: M
58+ op:: T
59+ Mean (m:: M ; name= :mean ) where {M<: Metric } = let op = acc_op (m); new {M, typeof(op)} (name, m, op) end
60+ Mean (m:: M , op:: T ; name= :mean ) where {M<: Metric , T} = new {M, T} (name, m, op)
61+ end
62+ name (f:: Mean ) = f. name
63+
64+ function evaluate (f:: Mean , sys, dt1:: DataFrame , dt2:: DataFrame ; init = nothing )
65+ _require_joint_solution (sys, dt1, dt2)
66+ measured_cols = setdiff (intersect (names (dt1), names (dt2)), " timestamp" )
67+ output = isnothing (init) ? initial_output (f. metric) : init
68+ for (row1, row2) in Iterators. zip (eachrow (dt1[! , measured_cols]), eachrow (dt2[! , measured_cols]))
69+ new_output = compare (f. metric, sys, row1, row2)
70+ output = f. op (output, new_output)
71+ end
72+ return name (f) => Dict (outputs (f. metric) .=> mean .(output))
73+ end
74+
75+ acc_op (m:: Metric ) = (a,b) -> b # throw away the prior state by default
76+
77+ name (m:: Metric ):: Symbol = throw (" Unimplemented name for metric $(typeof (m)) " )
78+ outputs (m:: Metric ):: Vector{Symbol} = throw (" Unimplemented outputs for metric $(typeof (m)) " )
79+ initial_output (m:: Metric ) = throw (" Unimplemented metric output initialization for $(typeof (m)) ; specify an explicit initalization" )
80+
81+ compare (m:: Metric , sys, state_a, state_b) = throw (" Unimplemented discrete metric comparison for $(typeof (m)) " )
82+ compare! (o, m:: Metric , sys, state_a, state_b) = o .= compare (m, sys, state_a, state_b)
83+
84+
85+ struct L∞ <: Metric
86+ name:: Union{Nothing, Symbol}
87+ L∞ (;name:: Union{Nothing, Symbol} = nothing ) = new (name)
88+ end
89+ acc_op (:: L ∞) = (a,b) -> max .(a,b)
90+ name (m:: L ∞) = m. name
91+ initial_output (m:: L ∞) = [0.0 ]
92+ outputs (m:: L ∞) = [name (m)]
93+ compare (m:: L ∞, sys, state_a, state_b) = [maximum (abs .(collect (state_a) .- collect (state_b)))]
94+
95+ struct L2 <: Metric
96+ name:: Union{Nothing, Symbol}
97+ end
98+ acc_op (:: L2 ) = (a,b) -> a .+ b
99+ name (m:: L2 ) = m. name
100+ initial_output (m:: L2 ) = [0.0 ]
101+ outputs (m:: L2 ) = [name (m)]
102+ compare (m:: L2 , sys, state_a, state_b) = [norm (collect (state_a) .- collect (state_b))]
103+
104+ export Metric, L∞, L2
0 commit comments