I am new user of Julia and I want to use it for solving PDEs and ODEs numerically. I am trying to run examples that are available in Julia website or GitHub but I get error.
For instance I want to run this example:
using OrdinaryDiffEq, ModelingToolkit, DiffEqOperators
# Method of Manufactured Solutions: exact solution
u_exact = (x,t) -> exp.(-t) * cos.(x)
# Parameters, variables, and derivatives
#parameters t x
#variables u(..)
Dt = Differential(t)
Dxx = Differential(x)^2
# 1D PDE and boundary conditions
eq = Dt(u(t,x)) ~ Dxx(u(t,x))
bcs = [u(0,x) ~ cos(x),
u(t,0) ~ exp(-t),
u(t,1) ~ exp(-t) * cos(1)]
# Space and time domains
domains = [t ∈ IntervalDomain(0.0,1.0),
x ∈ IntervalDomain(0.0,1.0)]
# PDE system
pdesys = PDESystem(eq,bcs,domains,[t,x],[u(t,x)])
# Method of lines discretization
dx = 0.1
order = 2
discretization = MOLFiniteDifference([x=>dx],t)
# Convert the PDE problem into an ODE problem
prob = discretize(pdesys,discretization)
# Solve ODE problem
using OrdinaryDiffEq
sol = solve(prob,Tsit5(),saveat=0.2)
# Plot results and compare with exact solution
x = (0:dx:1)[2:end-1]
t = sol.t
using Plots
plt = plot()
for i in 1:length(t)
plot!(x,sol.u[i],label="Numerical, t=$(t[i])")
scatter!(x, u_exact(x, t[i]),label="Exact, t=$(t[i])")
end
display(plt)
savefig("plot.png")
But I get this error:
UndefKeywordError: keyword argument name not assigned
Stacktrace:
[1] PDESystem(eqs::Equation, bcs::Vector{Equation}, domain::Vector{Symbolics.VarDomainPairing}, ivs::Vector{Num}, dvs::Vector{Num}, ps::SciMLBase.NullParameters) (repeats 2 times)
# ModelingToolkit C:\Users\rm18124.julia\packages\ModelingToolkit\57XKa\src\systems\pde\pdesystem.jl:75
[2] top-level scope
# In[32]:22
[3] eval
# .\boot.jl:373 [inlined]
[4] include_string(mapexpr::typeof(REPL.softscope), mod::Module, code::String, filename::String)
# Base .\loading.jl:
1196
I double checked the PDESystem and it looks fine, any help please?
Thanks
You forgot to ensure the pdesys was named as in the docs, i.e. #named pdesys = PDESystem(eq,bcs,domains,[t,x],[u(t,x)])
Related
I am trying to implement a model with a custom loss function in the Flux.jl package. I include the code for a simplified model, but the error stays the same.
I have an interpolator which takes a scalar value and returns a 2x2 matrix. The goal of my model is to use 3 observations to find the best point to evaluate the interpolator at. For this I wrote a custom loss function that computes the suggested evalutation_point and evaluates the interpolator at this point. Then the interpolated result is compared to the true solution from the dataset.
using Flux, Zygote
using LinearAlgebra
using Interpolations
##
# create interpolator
x = LinRange(0,1,10)
y = [rand(2,2) for i in 1:10]
itp = interpolate(y, BSpline(Linear())) |> i -> scale(i, x)
# create training set
training_set = [(rand(3), rand(2,2)) for i in 0:0.2:1]
#build the model
model = Chain(Dense(3,1),i-> clamp(i[1],0,1))
opt = Descent()
ps = Flux.params(model)
function loss(evaluation_point, solution)
interpolated = itp(model(evaluation_point))
return norm(interpolated - solution)
end
# training NOK
n_epochs = 100
for epoch in 1:n_epochs
Flux.train!(loss, ps, training_set, opt)
println(sum([loss_fnc(i[1],i[2]) for i in training_set]))
end
This returns the following error:
ERROR: DimensionMismatch("matrix A has dimensions (2,2), vector B has length 1")
Stacktrace:
[1] generic_matvecmul!(C::Vector{Matrix{Float64}}, tA::Char, A::Matrix{Float64}, B::StaticArrays.SVector{1, Matrix{Float64}}, _add::LinearAlgebra.MulAddMul{true, true, Bool, Bool})
# LinearAlgebra C:\Users\thega\AppData\Local\Programs\Julia-1.7.2\share\julia\stdlib\v1.7\LinearAlgebra\src\matmul.jl:713
[2] mul!
# C:\Users\thega\AppData\Local\Programs\Julia-1.7.2\share\julia\stdlib\v1.7\LinearAlgebra\src\matmul.jl:81 [inlined]
[3] mul!
# C:\Users\thega\AppData\Local\Programs\Julia-1.7.2\share\julia\stdlib\v1.7\LinearAlgebra\src\matmul.jl:275 [inlined]
[4] *
# C:\Users\thega\AppData\Local\Programs\Julia-1.7.2\share\julia\stdlib\v1.7\LinearAlgebra\src\matmul.jl:51 [inlined]
[5] interpolate_pullback
# C:\Users\thega\.julia\packages\Interpolations\Glp9h\src\chainrules\chainrules.jl:13 [inlined]
[6] ZBack
# C:\Users\thega\.julia\packages\Zygote\H6vD3\src\compiler\chainrules.jl:204 [inlined]
[7] Pullback
# c:\Users\thega\Desktop\Question\main.jl:21 [inlined]
[8] (::typeof(∂(loss)))(Δ::Float64)
# Zygote C:\Users\thega\.julia\packages\Zygote\H6vD3\src\compiler\interface2.jl:0
[9] #212
# C:\Users\thega\.julia\packages\Zygote\H6vD3\src\lib\lib.jl:203 [inlined]
[10] #1750#back
# C:\Users\thega\.julia\packages\ZygoteRules\AIbCs\src\adjoint.jl:67 [inlined]
[11] Pullback
# C:\Users\thega\.julia\packages\Flux\0c9kI\src\optimise\train.jl:102 [inlined]
[12] (::typeof(∂(λ)))(Δ::Float64)
# Zygote C:\Users\thega\.julia\packages\Zygote\H6vD3\src\compiler\interface2.jl:0
[13] (::Zygote.var"#93#94"{Params, typeof(∂(λ)), Zygote.Context})(Δ::Float64)
# Zygote C:\Users\thega\.julia\packages\Zygote\H6vD3\src\compiler\interface.jl:357
[14] gradient(f::Function, args::Params)
# Zygote C:\Users\thega\.julia\packages\Zygote\H6vD3\src\compiler\interface.jl:76
[15] macro expansion
# C:\Users\thega\.julia\packages\Flux\0c9kI\src\optimise\train.jl:101 [inlined]
[16] macro expansion
# C:\Users\thega\.julia\packages\Juno\n6wyj\src\progress.jl:134 [inlined]
[17] train!(loss::Function, ps::Params, data::Vector{Tuple{Vector{Float64}, Matrix{Float64}}}, opt::Descent; cb::Flux.Optimise.var"#40#46")
# Flux.Optimise C:\Users\thega\.julia\packages\Flux\0c9kI\src\optimise\train.jl:99
[18] train!(loss::Function, ps::Params, data::Vector{Tuple{Vector{Float64}, Matrix{Float64}}}, opt::Descent)
# Flux.Optimise C:\Users\thega\.julia\packages\Flux\0c9kI\src\optimise\train.jl:97
[19] top-level scope
# c:\Users\thega\Desktop\Question\main.jl:28
So something about a dimension mismatch, but the evaluation of the loss function works fine.
loss(training_set[1][1], training_set[1][2])
I play around a bit and found that the problem is the gradient computation:
gradient(loss , training_set[1][1], training_set[1][2])
The problem can be found in the training set, with your provided example, check this out:
julia> training_set[1][1]
3-element Vector{Float64}:
0.5093876656425886
0.05272770864628318
0.7651982428671759
julia> training_set[1][2]
2×2 Matrix{Float64}:
0.0691616 0.55414
0.5153 0.654379
For the model, the input is: as x a 2-element vector, and the model should learn to return a 2x2 matrix as y. However, your model does not do that:
julia> model(training_set[1][1])
0.6585413f0 (tracked)
It only returns an instance, due to the definition of the model, which in this case model = Chain(Dense(3,1),i-> clamp(i[1],0,1)) turns to be only a Chain(Dense(3, 1), #7), which means that has a 3 element vector as an input, and returns 1 (and only 1) instance.
Solutions:
redefine your y as a 1 element output for each x
redefine your model (it will be more complicated as you want to return a 2x2 matrix). An example of this would be the following model:
julia> model = Chain(
Dense(3, 4),
x -> reshape(x, (2, 2))
)
But then, you should figure out how to adapt your interpolation code to work
I was not able to fix the problem. My guess is that Interpolations.jl is not compatible with Zygote.jl. A possible workaround I found, was writing a custom interpolations class and function. I include a working example if anyone is interested:
using Flux, Zygote
using LinearAlgebra
using Interpolations
# create a custom linear splines class
struct CustomInterpolator
x::Vector
y::Vector
function CustomInterpolator(x,y)
#assert issorted(x)
return new(x,y)
end
end
function custom_interpolate(citp::CustomInterpolator, x::Number)
left_value, right_value = 0, 0
left_index, right_index = 1, 1
# check bound
if x > citp.x[end] || x < citp.x[1]
#error "Out of bounds"
throw(DomainError(x))
end
#find the right indices
for (i,v) in enumerate(citp.x)
if left_value > x
right_value = v
right_index = i
break
end
left_value = v
left_index = i
end
# do a linear inter interpolation between the two selected indices
interpolated_value = (1 - (x - left_value)/(right_value - left_value)) * citp.y[left_index] + (x - left_value)/(right_value - left_value) * citp.y[right_index]
return interpolated_value
end
##
# create custom interpolator
x = LinRange(0,1,2)
y = [zeros(2,2), ones(2,2)]
citp = CustomInterpolator(x,y)
# create training set
training_set = [(ones(3)*i, ones(2,2) - i*ones(2,2)) for i in 0:0.2:1]
#build the model
model = Chain(Dense(3,3), Dense(3,1), i-> clamp(i[1],0,1), i->custom_interpolate(citp,i))
opt = ADAM()
ps = Flux.params(model)
loss(x,y) = Flux.mse(model(x), y)
# training
n_epochs = 1000
for epoch in 1:n_epochs
Flux.train!(loss, ps, training_set, opt)
println(sum([loss(i[1],i[2]) for i in training_set]))
end
I have following Inputs:
Inputs <- seq(2,7.7,0.3)
Weights <- paste("w",sep="_",seq(1:20))
And the following equations:
sum(Weights * Inputs) == 4.8
sum(Weights) == 1
min(sum(Weights^2))
Can someone explain how I get a solution for Weights? Thanks!
You can use the optim function. This relies on being able to specify a function that produces a single scalar output which is minimized when the conditions are met. In your case, the function might look like this:
constraints <- function(W) (sum(W * Inputs) - 4.8)^2 + (sum(Weights) - 1)^2
So to solve it we can do:
Weights <- optim(rep(0.05, 20), constraints, method = "BFGS")$par
Which gives us the following result:
Weights
#> [1] 0.04981143 0.04978314 0.04975486 0.04972657 0.04969828 0.04967000 0.04964171
#> [8] 0.04961343 0.04958514 0.04955685 0.04952857 0.04950028 0.04947200 0.04944371
#> [15] 0.04941543 0.04938714 0.04935885 0.04933057 0.04930228 0.04927400
sum(Weights * Inputs)
#> [1] 4.8
sum(Weights)
#> [1] 0.9908542
Obviously, this is a numeric optimization with a 20-dimensional input, so it doesn't perfectly converge to a sum of 1 with the given starting values.
I am building a DataFrame row by row and then running a regression on it. For simplicity, the code is:
using DataFrames
using GLM
df = DataFrame(response = Number[])
for i in 1:10
df = vcat(df, DataFrame(response = rand()))
end
fit(LinearModel, #formula(response ~ 1), df)
I get the error:
ERROR: LoadError: MethodError: Cannot `convert` an object of type Array{Number,1} to an object of type GLM.LmResp
This may have arisen from a call to the constructor GLM.LmResp(...),
since type constructors fall back to convert methods.
Stacktrace:
[1] fit(::Type{GLM.LinearModel}, ::Array{Float64,2}, ::Array{Number,1}) at ~/.julia/v0.6/GLM/src/lm.jl:140
[2] #fit#44(::Dict{Any,Any}, ::Array{Any,1}, ::Function, ::Type{GLM.LinearModel}, ::StatsModels.Formula, ::DataFrames.DataFrame) at ~/.julia/v0.6/StatsModels/src/statsmodel.jl:72
[3] fit(::Type{GLM.LinearModel}, ::StatsModels.Formula, ::DataFrames.DataFrame) at ~/.julia/v0.6/StatsModels/src/statsmodel.jl:66
[4] include_from_node1(::String) at ./loading.jl:576
[5] include(::String) at ./sysimg.jl:14
while loading ~/test.jl, in expression starting on line 10
The call to the linear regression is very similar to regression in "Introducing Julia":
linearmodel = fit(LinearModel, #formula(Y1 ~ X1), anscombe)
What is the problem?
After a few hours, I realized that GLM requires concrete types and Number is an abstract type (even though the documentation for GLM.LmResp says little about this at the time of this writing, only "Encapsulates the response for a linear model"). The solution is to change the declaration to a concrete type, such as Float64:
using DataFrames
using GLM
df = DataFrame(response = Float64[])
for i in 1:10
df = vcat(df, DataFrame(response = rand()))
end
fit(LinearModel, #formula(response ~ 1), df)
Output:
StatsModels.DataFrameRegressionModel{GLM.LinearModel{GLM.LmResp{Array{Float64,1}},GLM.DensePredChol{Float64,Base.LinAlg.Cholesky{Float64,Array{Float64,2}}}},Array{Float64,2}}
Formula: response ~ +1
Coefficients:
Estimate Std.Error t value Pr(>|t|)
(Intercept) 0.408856 0.0969961 4.21518 0.0023
The type has to be concrete, e.g. the abstract type Real with df = DataFrame(response = Real[]) fails with a more helpful error message:
ERROR: LoadError: `float` not defined on abstractly-typed arrays; please convert to a more specific type
Alternatively, you can convert to Real after building the dataframe:
using DataFrames
using GLM
df = DataFrame(response = Number[])
for i in 1:10
df = vcat(df, DataFrame(response = rand()))
end
df2 = DataFrame(response = map(Real, df[:response]))
fit(LinearModel, #formula(response ~ 1), df2)
This works because converting to Real actually converts to Float64:
julia> typeof(df2[:response])
Array{Float64,1}
I filed an issue with GLM to improve the error message.
I have created a constparty decision tree (customized split rules) and print out the tree result. The result looks like this:
Fitted party:
[1] root
| [2] value.a < 1651: 0.067 (n = 1419, err = 88.6)
| [3] value.a >= 1651: 0.571 (n = 7, err = 1.7)
I am trying to extract terminal node info
(the yval: 0.067 and 0.571;
the n on each node: 1419 and 7;
and err: 88.6 and 1.7) and put them into a list while having the corresponding node id (node ID 2 and 3) so that I can utilize those info later.
I have been looking into partykit functions for a while and could not find a function that could help me extracting those info I just listed.
Could someone help me please? Thank you!
As usual there are several approaches to obtain the information you are looking for. The technical way for extracting the info stored in a particular node is to use nodeapply(object, ids, info_node) where info_node returns a list of information stored in the respective node.
However, in the terminal nodes of constparty objects there is nothing stored. Instead, the whole distribution of the response by fitted node is stored and can be extracted by fitted(object). This contains a data frame with the observed (response) the (fitted) node and the observation (weights) (if any). And then you can easily use tapply() or aggregate() or something like that to compute node-wise means etc.
Alternatively, you can convert the constparty object to a simpleparty object which stores the printed information in the nodes and extract it.
A worked example for both strategies is a simple regression tree for the cars data:
library("partykit")
data("cars", package = "datasets")
ct <- ctree(dist ~ speed, data = cars)
Then you can easily compute node-wise means by
with(fitted(ct), tapply(`(response)`, `(fitted)`, mean))
## 3 4 5
## 18.20000 39.75000 65.26316
Of course, you can replace mean by any other summary statistic you are interested in.
The nodeapply() for the simpleparty can be obtained by:
nodeapply(as.simpleparty(ct), ids = nodeids(ct, terminal = TRUE), info_node)
## $`3`
## $`3`$prediction
## [1] 18.2
##
## $`3`$n
## n
## 15
##
## $`3`$error
## [1] 1176.4
##
## $`3`$distribution
## NULL
##
## $`3`$p.value
## NULL
##
##
## $`4`
## $`4`$prediction
## [1] 39.75
## ...
I have a probably really basic question concerning the possibility to solve functions in R, but to know the answer would really help to understand R better.
I have following equation:
0=-100/(1+r)+(100-50)/(1+r)^2+(100-50)/(1+r)^3+...(100-50)/(1+r)^10
How can I solve this equation in R finding the variable r?
I tried sth. like this:
n <- c(2:10)
0 = -100/(r+1)+sum((100-50)/((1+r)^n))
But got an error message:
Error in 0 = -100/(r + 1) + sum((100 - 50)/((1 + r)^n)) :
invalid (do_set) left-hand side to assignment
What's the problem and how can I find r?
There are plenty of optimization and root finding libraries for R link here. But in native R:
fnToFindRoot = function(r) {
n <- c(2:10)
return(abs(-100/(r+1)+sum((100-50)/((1+r)^n))))
}
# arbitrary starting values
r0 = 0
# minimise the function to get the parameter estimates
rootSearch = optim(r0, fnToFindRoot,method = 'BFGS', hessian=TRUE)
str(rootSearch)
fnToFindRoot(rootSearch$par)
That function is very volatile. If you are willing to bracket the root, you are probably better off with uniroot:
fnToFindRoot = function(r,a) {
n <- c(2:10)
return((-100/(r+1)+sum((100-50)/((1+r)^n)))-a)
}
str(xmin <- uniroot(fnToFindRoot, c(-1E6, 1E6), tol = 0.0001, a = 0))
The a argument is there so you can look for a root to any arbitrary value.
Try bisection. This converges to r = 0.4858343 in 25 iterations:
library(pracma)
bisect(function(r) -100/(1+r) + sum(50/(r+1)^seq(2, 10)), 0, 1)
giving:
$root
[1] 0.4858343
$f.root
[1] 8.377009e-07
$iter
[1] 25
$estim.prec
[1] 1.490116e-08
Let x = 1/(1+r), so your equation should be:
0-100x + 50x^2 + 50x^3 + ... + 50x^10 = 0.
then in R:
x <- polyroot(c(0, -100, rep(50, 9)))
(r <- 1/x - 1)
Here is the answer:
[1] Inf+ NaNi 0.4858344-0.0000000i -1.7964189-0.2778635i
[4] -0.3397136+0.6409961i -0.3397136-0.6409961i -1.4553556-0.7216708i
[7] -0.9014291+0.8702213i -0.9014291-0.8702213i -1.7964189+0.2778635i
[10] -1.4553556+0.7216708i