Skip to content

Commit bcd71a0

Browse files
Merge pull request #922 from visr/moshi
Replace Expronicon with Moshi
2 parents 4fd028f + ca0e3d2 commit bcd71a0

File tree

8 files changed

+172
-38
lines changed

8 files changed

+172
-38
lines changed

.typos.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
[default.extend-words]
2-
ND = "ND"
2+
ND = "ND"
3+
Cachable = "Cachable"

Project.toml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@ ConstructionBase = "187b0558-2788-49d3-abe0-74a17ed4e7c9"
1212
Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b"
1313
DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"
1414
EnumX = "4e289a0a-7415-4d19-859d-a7e5c4648b56"
15-
Expronicon = "6b7a57c9-7cc1-4fdf-b7f5-e857abae3636"
1615
FunctionWrappersWrappers = "77dc65aa-8811-40c2-897b-53d922fa7daf"
1716
IteratorInterfaceExtensions = "82899510-4779-5014-852e-03e436cf321d"
1817
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
1918
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
2019
Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a"
20+
Moshi = "2e0e35c7-a2e4-4343-998d-7ef72827ed2d"
2121
PrecompileTools = "aea7be01-6a6a-4083-8856-8a6e6704d82a"
2222
Preferences = "21216c6a-2e73-6563-6e65-726566657250"
2323
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
@@ -34,6 +34,7 @@ SymbolicIndexingInterface = "2efcf032-c050-4f8e-a9bb-153293bab1f5"
3434
[weakdeps]
3535
ChainRules = "082447d4-558c-5d27-93f4-14fc19e9eca2"
3636
ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"
37+
MLStyle = "d8e11817-5142-5d16-987a-aa16d5891078"
3738
Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a"
3839
PartialFunctions = "570af359-4316-4cb7-8c74-252c00c2016b"
3940
PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0"
@@ -43,6 +44,7 @@ Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f"
4344

4445
[extensions]
4546
SciMLBaseChainRulesCoreExt = "ChainRulesCore"
47+
SciMLBaseMLStyleExt = "MLStyle"
4648
SciMLBaseMakieExt = "Makie"
4749
SciMLBasePartialFunctionsExt = "PartialFunctions"
4850
SciMLBasePyCallExt = "PyCall"
@@ -62,14 +64,15 @@ DataFrames = "1.6"
6264
Distributed = "1.10"
6365
DocStringExtensions = "0.9"
6466
EnumX = "1"
65-
Expronicon = "0.8"
6667
ForwardDiff = "0.10.36"
6768
FunctionWrappersWrappers = "0.1.3"
6869
IteratorInterfaceExtensions = "^1"
6970
LinearAlgebra = "1.10"
7071
Logging = "1.10"
72+
MLStyle = "0.4.17"
7173
Makie = "0.20, 0.21, 0.22"
7274
Markdown = "1.10"
75+
Moshi = "0.3"
7376
NonlinearSolve = "3, 4"
7477
PartialFunctions = "1.1"
7578
PrecompileTools = "1.2"
@@ -99,6 +102,7 @@ ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"
99102
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
100103
DelayDiffEq = "bcd4f6db-9728-5f36-b5f7-82caef46ccdb"
101104
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
105+
MLStyle = "d8e11817-5142-5d16-987a-aa16d5891078"
102106
NonlinearSolve = "8913a72c-1f9b-4ce2-8d82-65094dcecaec"
103107
OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed"
104108
PartialFunctions = "570af359-4316-4cb7-8c74-252c00c2016b"
@@ -116,4 +120,4 @@ UnicodePlots = "b8865327-cd53-5732-bb35-84acbb429228"
116120
Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f"
117121

118122
[targets]
119-
test = ["Pkg", "Plots", "UnicodePlots", "SafeTestsets", "Serialization", "Test", "StableRNGs", "StaticArrays", "StochasticDiffEq", "Aqua", "Zygote", "PartialFunctions", "DataFrames", "NonlinearSolve", "OrdinaryDiffEq", "ForwardDiff", "Tables"]
123+
test = ["Pkg", "Plots", "UnicodePlots", "SafeTestsets", "Serialization", "Test", "StableRNGs", "StaticArrays", "StochasticDiffEq", "Aqua", "Zygote", "PartialFunctions", "DataFrames", "NonlinearSolve", "OrdinaryDiffEq", "ForwardDiff", "Tables", "MLStyle"]

ext/SciMLBaseMLStyleExt.jl

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
"""
2+
The SciMLBaseMLStyleExt module provides backwards compatibility for those using MLStyle
3+
to pattern match the Clocks algebraic data type (ADT).
4+
5+
Before this ADT was made using Expronomicon, which directly supported MLStyle.
6+
Now that the ADT is made using Moshi, we need to define MLStyle custom patterns.
7+
Using Moshi pattern matching is recommended over relying on this package extension.
8+
"""
9+
module SciMLBaseMLStyleExt
10+
11+
using SciMLBase: TimeDomain, ContinuousClock, SolverStepClock, PeriodicClock
12+
using MLStyle: MLStyle
13+
using MLStyle.AbstractPatterns: literal, wildcard, PComp, BasicPatterns, decons
14+
using Moshi.Data: isa_variant
15+
16+
# This makes Singletons also work without parentheses in matches
17+
MLStyle.is_enum(::Type{ContinuousClock}) = true
18+
MLStyle.is_enum(::Type{SolverStepClock}) = true
19+
function MLStyle.pattern_uncall(::Type{ContinuousClock}, self::Function, _, _, _)
20+
literal(ContinuousClock())
21+
end
22+
MLStyle.pattern_uncall(T::TimeDomain, self::Function, _, _, _) = literal(T())
23+
24+
function MLStyle.pattern_uncall(::Type{SolverStepClock}, self::Function, _, _, _)
25+
literal(SolverStepClock())
26+
end
27+
28+
function periodic_clock_pattern(c)
29+
if c isa TimeDomain && isa_variant(c, PeriodicClock)
30+
(c.dt, c.phase)
31+
else
32+
# These values are used in match results, but they shouldn't.
33+
# This means that any wildcard pattern will be `nothing`, see broken test.
34+
(nothing, nothing)
35+
end
36+
end
37+
38+
function MLStyle.pattern_uncall(
39+
::Type{PeriodicClock}, self::Function, type_params, type_args, args)
40+
@assert isempty(type_params)
41+
@assert isempty(type_args)
42+
n_args = length(args)
43+
44+
trans(expr) = Expr(:call, periodic_clock_pattern, expr)
45+
type_infer(_...) = Any
46+
47+
extract = if n_args <= 1
48+
(expr::Any, i::Int, ::Any, ::Any) -> expr
49+
else
50+
(expr::Any, i::Int, ::Any, ::Any) -> Core._expr(:ref, expr, i)
51+
end
52+
53+
comp = PComp(
54+
"PeriodicClock(c)", type_infer; view = BasicPatterns.SimpleCachablePre(trans))
55+
56+
ps = if n_args === 0
57+
[]
58+
elseif n_args === 1
59+
[self(Expr(:call, Some, args[1]))]
60+
elseif n_args === 2
61+
if args[2] == :(_...)
62+
[self(args[1]), wildcard]
63+
else
64+
[self(args[1]), self(args[2])]
65+
end
66+
else
67+
error("too many arguments")
68+
end
69+
70+
decons(comp, extract, ps)
71+
end
72+
73+
end

src/SciMLBase.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ import RuntimeGeneratedFunctions
2323
import EnumX
2424
import ADTypes: ADTypes, AbstractADType
2525
import Accessors: @set, @reset, @delete, @insert
26-
using Expronicon.ADT: @match
26+
using Moshi.Data: @data
27+
using Moshi.Match: @match
2728

2829
using Reexport
2930
using SciMLOperators

src/clock.jl

Lines changed: 15 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,20 @@
1-
module Clocks
2-
3-
export TimeDomain
4-
5-
using Expronicon.ADT: variant_type, @adt, @match
6-
7-
@adt TimeDomain begin
8-
Continuous
1+
@data Clocks begin
2+
ContinuousClock
93
struct PeriodicClock
104
dt::Union{Nothing, Float64, Rational{Int}}
115
phase::Float64 = 0.0
126
end
137
SolverStepClock
148
end
159

10+
# for backwards compatibility
11+
const TimeDomain = Clocks.Type
12+
using .Clocks: ContinuousClock, PeriodicClock, SolverStepClock
13+
const Continuous = ContinuousClock()
1614
(clock::TimeDomain)() = clock
1715

1816
Base.Broadcast.broadcastable(d::TimeDomain) = Ref(d)
1917

20-
const DiscriminatorType = typeof(variant_type(Continuous))
21-
22-
function Base.write(io::IO, x::DiscriminatorType)
23-
write(io, Base.reinterpret(UInt32, x))
24-
end
25-
26-
function Base.read(io::IO, ::Type{DiscriminatorType})
27-
Base.reinterpret(DiscriminatorType, read(io, UInt32))
28-
end
29-
30-
end
31-
32-
using .Clocks
33-
3418
"""
3519
Clock(dt)
3620
Clock()
@@ -57,27 +41,27 @@ filters.
5741
""" SolverStepClock
5842

5943
isclock(c) = @match c begin
60-
PeriodicClock(_...) => true
44+
PeriodicClock() => true
6145
_ => false
6246
end
6347

6448
issolverstepclock(c) = @match c begin
65-
&SolverStepClock => true
49+
SolverStepClock() => true
6650
_ => false
6751
end
6852

6953
iscontinuous(c) = @match c begin
70-
&Continuous => true
54+
ContinuousClock() => true
7155
_ => false
7256
end
7357

7458
is_discrete_time_domain(c) = !iscontinuous(c)
7559

7660
function first_clock_tick_time(c, t0)
7761
@match c begin
78-
PeriodicClock(dt, _...) => ceil(t0 / dt) * dt
79-
&SolverStepClock => t0
80-
&Continuous => error("Continuous is not a discrete clock")
62+
PeriodicClock(dt) => ceil(t0 / dt) * dt
63+
SolverStepClock() => t0
64+
ContinuousClock() => error("ContinuousClock() is not a discrete clock")
8165
end
8266
end
8367

@@ -92,13 +76,13 @@ function canonicalize_indexed_clock(ic::IndexedClock, sol::AbstractTimeseriesSol
9276
c = ic.clock
9377

9478
return @match c begin
95-
PeriodicClock(dt, _...) => ceil(sol.prob.tspan[1] / dt) * dt .+ (ic.idx .- 1) .* dt
96-
&SolverStepClock => begin
79+
PeriodicClock(dt) => ceil(sol.prob.tspan[1] / dt) * dt .+ (ic.idx .- 1) .* dt
80+
SolverStepClock() => begin
9781
ssc_idx = findfirst(eachindex(sol.discretes)) do i
9882
!isa(sol.discretes[i].t, AbstractRange)
9983
end
10084
sol.discretes[ssc_idx].t[ic.idx]
10185
end
102-
&Continuous => sol.t[ic.idx]
86+
ContinuousClock() => sol.t[ic.idx]
10387
end
10488
end

src/scimlfunctions.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1805,7 +1805,7 @@ struct HomotopyNonlinearFunction{iip, specialize, F, P, Q, D} <:
18051805
In the above example, it will be the function:
18061806
18071807
```julia
1808-
functon polynomialize(u, p)
1808+
function polynomialize(u, p)
18091809
x, y = u
18101810
return [sin(x^2), log(x + y)]
18111811
end
@@ -1837,7 +1837,7 @@ struct HomotopyNonlinearFunction{iip, specialize, F, P, Q, D} <:
18371837
end
18381838
```
18391839
1840-
There are of course an infinite number of such functions due to the presence of `sin`.
1840+
There are of course an infinite number of such functions due to the presence of `sin`.
18411841
This example chooses to return the roots in the interval `[-π/2, π/2]`.
18421842
"""
18431843
unpolynomialize::Q

test/clock.jl

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
using Test
2+
using SciMLBase
3+
using SciMLBase: Clock, PeriodicClock, Continuous, ContinuousClock, SolverStepClock,
4+
first_clock_tick_time, IndexedClock, canonicalize_indexed_clock
5+
using MLStyle: @match
6+
7+
@testset "Clock" begin
8+
@test PeriodicClock(nothing, 0.2) isa TimeDomain
9+
@test SolverStepClock() isa TimeDomain
10+
@test ContinuousClock() isa TimeDomain
11+
@test Continuous() isa TimeDomain
12+
@test Continuous === ContinuousClock()
13+
14+
@test Clock(1) isa TimeDomain
15+
@test Clock(24.0; phase = 0.1) == PeriodicClock(24.0, 0.1)
16+
@test Clock(1 // 24) == PeriodicClock(1 // 24, 0.0)
17+
@test Clock(; phase = 0.2) == PeriodicClock(nothing, 0.2)
18+
19+
@test isclock(PeriodicClock(; dt = 1.0))
20+
@test !isclock(Continuous())
21+
@test !isclock(SolverStepClock())
22+
23+
@test !issolverstepclock(PeriodicClock(; dt = 1.0))
24+
@test !issolverstepclock(Continuous())
25+
@test issolverstepclock(SolverStepClock())
26+
27+
@test !iscontinuous(PeriodicClock(; dt = 1.0))
28+
@test iscontinuous(Continuous())
29+
@test !iscontinuous(SolverStepClock())
30+
31+
@test is_discrete_time_domain(PeriodicClock(; dt = 1.0))
32+
@test !is_discrete_time_domain(Continuous())
33+
@test is_discrete_time_domain(SolverStepClock())
34+
35+
@test first_clock_tick_time(PeriodicClock(; dt = 2.0), 5.0) === 6.0
36+
@test_throws ErrorException first_clock_tick_time(Continuous(), 5.0)
37+
@test first_clock_tick_time(SolverStepClock(), 5.0) === 5.0
38+
39+
ic = Clock(1)[5]
40+
@test ic === IndexedClock(Clock(1), 5)
41+
end
42+
43+
@testset "MLStyle" begin
44+
sampletime(c) = @match c begin
45+
PeriodicClock(dt, _...) => dt
46+
_ => nothing
47+
end
48+
49+
@test sampletime(PeriodicClock(1 // 2, 3.14)) === 1 // 2
50+
@test sampletime(ContinuousClock()) === nothing
51+
@test sampletime(missing) === nothing
52+
53+
function clocktype(c)
54+
@match c begin
55+
Continuous() => "continuous"
56+
SolverStepClock() => "solver_step_clock"
57+
PeriodicClock(dt, phase) => (dt, phase)
58+
_ => "other"
59+
end
60+
end
61+
62+
@test clocktype(Continuous()) === "continuous"
63+
@test clocktype(ContinuousClock()) === "continuous"
64+
@test clocktype(Continuous) === "continuous"
65+
@test clocktype(SolverStepClock()) === "solver_step_clock"
66+
@test clocktype(PeriodicClock(1 // 2, 3.14)) === (1 // 2, 3.14)
67+
@test clocktype(pi)==="other" broken=true
68+
end

test/runtests.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ end
6666
@time @safetestset "Initialization" begin
6767
include("initialization.jl")
6868
end
69+
@time @safetestset "Clocks" begin
70+
include("clock.jl")
71+
end
6972
end
7073

7174
if !is_APPVEYOR &&

0 commit comments

Comments
 (0)