For a fairer comparison:
- Python has mean, median, and geometric_mean functions that could be employed like the Julia library functions.
- There’s no arbitrary recursion depth here.
For a fairer comparison:
Thanks.
It was a snip from the 1st comment I saw on Twitter.
Language syntax comparison suffer from the same type of problem as language performance comparison:
it depends on the ability of the person writing the code & on the actual language
The world would be better off if there was a “language elegance game” just like there is a Benchmarks Game.
Wooaah! remarkable! Could you explain how that magic trick works?
Fⁿ(x,n)= ∘(fill(F,n)...)(x)
Thanks!
∘ is the function composition function. ∘(f,g,h)(x)
turns into f(g(h(x)))
.
fill(F,n)
populates an array with n times the function F. They are composed by ∘ to form the function
x -> F(F(F(F...F(x)))...)
.
That’s is, you just call it with any argument you like.
Here’s my seven lines (excluding imports). It calculates and plots pursuit curves using DifferentialEquations.jl. I’ve renamed the pursuer and pursued to fox
and rabbit
for clarity. It’s kind of fun to play around with different paths for the rabbit to take. I have a parameter, k
, which alters the speed of the fox.
The fourth line beginning Rx, Ry,...
is a little goofy to fit within the space constraint, but it works and makes what I’m plotting clear as day.
using Plots, DifferentialEquations, LinearAlgebra
rabbit(t) = [cos(t), sin(t)] # Rabbit Running in a Circle
fox(u, k, t) = k * (rabbit(t) - u) / norm(rabbit(t)-u) # Fox chases rabbit at speed k
prob = ODEProblem(fox, [2.0, 0.0], (0.0, 10), 0.8)
sol = solve(prob, saveat=0.1);
Rx, Ry, Fx, Fy = [getindex.(vcat.(rabbit.(sol.t), sol.u), i) for i=1:4]
plot(Rx, Ry, label="Rabbit", lw=sol.t)
plot!(Fx, Fy, label="Fox", lw=sol.t, aspect_ratio=:equal)
Here’s a bonus animation:
Don’t forget one of the most evocative tools in Julia-land, the splat, which is needed to take the array-typed output of fill
and use the elements of that array to populate the individual arguments of ∘
.
I had to submit another one. I’m still leaning about the ApproxFun.jl package, so I’m happy to hear your feedback. This my first real trial of this package other than running a few of the examples.
The following 7 lines (excluding imports) calculates deflection, angle, moment, and shear of a uniformly loaded cantilevered beam and plots the results. This uses ApproxFun.jl find to solution of an ODE. The solution to this ODE is a function which defines the vertical displacement of the beam (under certain assumptions). Figure of the setup given below.
The line beginning B = ...
defines the boundary conditions. It’s a fourth order ODE so there are four boundary conditions. The vertical displacement and first derivative (ie, angle) are both zero at the fixed end of the beam (leftendpoint
). The moment and shear in the beam (2nd and 3rd) derivatives are zero at the free end of the beam (rightendpoint
).
The solution is generated on the next line, v = ...
. The boundary conditions, B
, are all set to 0 using zeros(4)...
. The differential equation is defined E*I*Derivative()^4
and gets set to a uniform load one(z)
(shown as q in the image above).
The rest is just plotting. It’s ugly again to fit into seven lines.
using ApproxFun, Plots
L, E, I = 12.0, 1.0, 1.0
d = 0..L
z = Fun(identity, d)
B = [[Evaluation(d,leftendpoint,k) for k=0:1]... ; [Evaluation(d,rightendpoint,k) for k=2:3]... ;]
v = [B; E*I*Derivative()^4] \ [ zeros(4)..., one(z)]
func_name = zip([v, v', v'', v'''], ["Deflection", "Angle", "Momement", "Shear"])
plot([plot(z, f, title=n, label="") for (f,n) in func_name]..., lw=3)
Result:
It’s easy enough to change the load function:
v = [B; E*I*Derivative()^4] \ [ zeros(4)..., sin(z)]
I haven’t really messed with the units to ensure the solution is scaled correctly, but it does seem to match the analytical solution of the uniformly loaded beam quite well. The comparison:
plot(z, v, lw=3, label="ApproxFun", title="Deflection | Comparison", legend=:left)
plot!(z-> (z^2)*(z^2 - 4*L*z + 6L^2)/(24*E*I), 0, L, lw=3, ls=:dash, label="Analytical")
Hey!
I love your code, but you should use
Fn(n) = reduce(\circ, fill(F, n))(x)
...
Fn(100)[1,1,2,3,5]
This is not my code, I saw it on Twitter. But yes it’s true that reducing is faster than splatting. But I think the point here was the clarity of the code not really the performance.
Your code still wastes memory to create the useless vector fill(F, n)
. Also, it doesn’t support the case n=0
. Just do this instead
Fn(x, n) = foldl((y,_) -> F(y), 1:n; init=x)
Edit: of course one can use a StaticArrays.SVector{3}
to sqeeze out the last bit of performance.
Just slightly over, but here’s a user-defined linear regression in Soss.jl:
julia> using Soss, MeasureTheory, SampleChainsDynamicHMC
julia> a = -0.2; b=2.0;
julia> x = randn(100); y = a .+ b .* x;
julia> m = @model x begin
a ~ Normal()
b ~ Normal()
y ~ For(x) do xj Normal(a + b * xj, 1) end
end;
julia> sample(DynamicHMCChain, m(x=x) | (;y=y))
4000-element MultiChain with 4 chains and schema (b = Float64, a = Float64)
(b = 1.985±0.097, a = -0.207±0.11)
It’s also standard notation for function iteration in the Dynamical Systems literature.
I agree it’s super neat!
Can you help me understand, what does “unsafe” & “type piracy” mean (in general & in this context)?
Btw, for composition I define a Nest(f,x0,n)
function like Mathematica:
Nest(f,x0,n) = (n == 0) ? x0 : (f∘Nest)(f,x0,n-1)
import Base.^;
h^(n::Integer) = x0 -> foldl( (x, _) -> h(x), 1:n; init = x0)
Type piracy is when a module defines a method but neither the function nor any of the method’s parameter types are created by that module.
Thanks, but what does that mean in this context?
Base.:^(f::Function, n) = n==1 ? f : f ∘ f^(n-1)
That snippet uses ^
and Function
, but both of those come from another module, Base. It would not be type piracy to define
struct CustomCounter end
Base.:^(f::Function, n::CustomCounter) = n==1 ? f : f ∘ f^(n-1)
because CustomCounter
is defined in this module.
It’s also super slow and wasteful
julia> @time @btime (F^(10^3))([1,1,2,3,5])
660.172 μs (2001 allocations: 218.89 KiB)
10.475864 seconds (29.82 M allocations: 3.087 GiB, 4.50% gc time, 0.24% compilation time)
3-element Vector{Float64}:
2.089057949736859
2.089057949736859
2.089057949736859
and doomed to fail miserably
julia> (F^(10^5))([1,1,2,3,5])
...
Bonus: if one is really concerned about the type piracy, then
(f::Function)↑(n::Integer) = x0 -> foldl((x, _) -> f(x), 1:n; init = x0)
is a suggestive alternative (↑
is \uparrow
).
Edit: in the first code snippet, @time @btime
is a typo. The intended code was just @time
, to take into consideration also the compilation time, instead of @btime
.
Try my version w/ ‘foldl’
Yes your version with foldl
is perfectly fine!
I like Pink noise. It’s is Gaussian noise with power spectral density 1/fᵅ. One can sample it with the inverse Fourier transform
# Make pink noise
using FFTW, Statistics
function pinknoise(m, n)
A = real(ifft((Complex{Float64}[i+j==2 ? 0. : (randn()+randn()*im)/sqrt((sin((i-1)*pi/m)^2 + sin((j-1)* pi/n)^2)) for i in 1:m, j in 1:n])))
real(A)/std(A)
end
A = pinknoise(500, 500)
It most spectacular property is that it is invariant under conformal mappings, functions that locally preserves angles, but not necessarily lengths, for example the map x + y*im -> ((x + y*im)/33)^2
.
# Make conformal map
getindexfc(A, i, j) = A[mod1(ceil(Int, i), size(A,1)), mod1(ceil(Int, j), size(A,2))]
finv(x) = reim(sqrt(x[1] + x[2]*im)*33) # use inverse for look-up
cfmap(A) = map(CartesianIndices(size(A))) do I
getindexfc(A, finv(I)...)
end
This is how it acts on a grid matrix G
:
[G cfmap(G)]
And here with pink noise
[A cfmap(A)]
PS: I took a bit extra effort to make it tiling, try [A A A; A A A; A A A]