# ModelingToolkit + matrix variables: defaults & 1×1 scalars

**URL:** <https://discourse.julialang.org/t/modelingtoolkit-matrix-variables-defaults-1x1-scalars/132825>\
**Category:** Modelling & Simulations\
**Created:** [October 2, 2025, 12:58pm UTC](https://discourse.julialang.org/t/modelingtoolkit-matrix-variables-defaults-1x1-scalars/132825 "2025-10-02T12:58:42Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![rasmuskovacs](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rasmuskovacs/32/218844_2.png) [@rasmuskovacs](https://discourse.julialang.org/u/rasmuskovacs)\
**Post date:** [October 2, 2025, 12:58pm UTC](https://discourse.julialang.org/t/modelingtoolkit-matrix-variables-defaults-1x1-scalars/132825/1 "2025-10-02T12:58:42Z")

</div>

Hi! I’m trying to use **ModelingToolkit** with matrix variables/parameters. I’ve got it working, but I need to be very careful about how I define things, and a couple behaviors feel non-intuitive. I’m wondering if I’m missing an idiomatic way to do this, or if what I’m seeing is expected.

## Minimal example 1 (works, but with quirks)

```julia
using ModelingToolkit
using ModelingToolkit: t_nounits as t, D_nounits as D
using OrdinaryDiffEq

@mtkmodel FOL begin
    @parameters begin
        A1[1:2,1:2] = [-1 0;
                        0 -2]
        v[1:1,1:2] = [1 2]
        k[1:2,1:1] = [1; 1]
    end
    @variables begin
        x(t)[1:2,1:1]
        x1(t) = 1
        x2(t) = 1
        u(t)
    end
    @equations begin
        x ~ [x1 x2]'
        D(x) ~ A1 * x * u
        u ~ (v * k)[1,1] # (v*k) is 1×1, so I index [1,1] to get a scalar for u
    end
end

@mtkcompile fol = FOL()
t2 = 3.0
prob = ODEProblem(fol, Dict(fol.x => [defaults(fol)[fol.x1], defaults(fol)[fol.x2]]), (0.0, t2))
sol = solve(prob, saveat=0.01)

```

**Observations:**

1. Defaults don’t seem to propagate from `x1(t)`,`x2(t)` to `x(t)` even though `x ~ [x1 x2]'`. With scalar variables I’m used to defaults flowing, but with matrix variables I have to manually supply `fol.x` in the `ODEProblem` init.
2. Because `v` is 1×2 and `k` is 2×1, `v*k` is 1×1 (a matrix), not a scalar. I have to do `(v*k)[1,1]` to feed `u(t)`.

## Minimal example 2 (also works, but more plumbing)

If I instead define `u(t)` as a 1×1 matrix, then I don’t need `[1,1]` on the RHS—but I have to provide `u`’s initial condition explicitly and in matrix shape:

```julia
using ModelingToolkit
using ModelingToolkit: t_nounits as t, D_nounits as D
using OrdinaryDiffEq

@mtkmodel FOL begin
    @parameters begin
        A1[1:2,1:2] = [-1 0;
                        0 -2]
        v[1:1,1:2] = [1 2]
        k[1:2,1:1] = [1; 1]
    end
    @variables begin
        x(t)[1:2,1:1]
        x1(t) = 1
        x2(t) = 1
        u(t)[1:1,1:1]
    end
    @equations begin
        x ~ [x1 x2]'
        D(x) ~ A1 * x * u
        u ~ (v * k)
    end
end

@mtkcompile fol = FOL()
t2 = 3.0
prob = ODEProblem(
    fol,
    Dict(
        fol.x => [defaults(fol)[fol.x1], defaults(fol)[fol.x2]],
        fol.u => 3 * ones(1,1) # must supply as 1×1 matrix
    ),
    (0.0, t2)
)
sol = solve(prob, saveat=0.01)

```

This works, but feels a bit clunky compared to the scalar case. Also now it would fail if I had

```julia
     D(x) ~ u * A1 * x

```

since the matrix dimension would be wrong (even though it works fine math wise since u is a scalar)

## Questions

1. **Default propagation for array variables:**  
Is it expected that defaults on the scalar components (`x1`, `x2`) do not propagate to the array variable (`x`) when using an equation like `x ~ [x1 x2]'`? Is there a recommended way to have defaults “flow” into `x(t)[…]` without manually building the vector for `ODEProblem`?

2. **Scalar vs 1×1 matrix ergonomics:**  
When expressions like `v*k` naturally produce a 1×1 matrix, what’s the idiomatic way to feed a scalar variable on the LHS? Is `(v*k)[1,1]` the intended approach?

3. **Defining array variables/parameters idiomatically:**  
Is there a best practice for declaring vectors vs 2D “column vectors” in `@variables`/`@parameters`? For example, should “state vectors” generally be `x(t)[1:2]` (1D) rather than `x(t)[1:2,1:1]` (2D)? Any pitfalls to be aware of when mixing 1D and 2D shapes in equations?

## Side note on Julia array syntax (source of confusion)

Just to show where part of my confusion came from:

```julia
[1 1] # 1×2 Matrix
[1,1] # 2-element Vector
[1;1] # 2-element Vector
ones(2,1) # 2×1 Matrix

```

I get why `[1 1]` vs `[1,1]` differ, but I still find myself tripping over it when switching between vector and matrix forms in symbolic equations.

Thanks a lot! Any guidance, docs pointers, or examples would be super appreciated.

---

<div class="post-metadata">

**Author:** ![ChrisRackauckas](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chrisrackauckas/32/77_2.png) [@ChrisRackauckas](https://discourse.julialang.org/u/ChrisRackauckas)\
**Post date:** [October 3, 2025, 11:01am UTC](https://discourse.julialang.org/t/modelingtoolkit-matrix-variables-defaults-1x1-scalars/132825/2 "2025-10-03T11:01:44Z")

</div>

> [@rasmuskovacs](#):
>
> Is it expected that defaults on the scalar components (`x1`, `x2`) do not propagate to the array variable (`x`) when using an equation like `x ~ [x1 x2]'`? Is there a recommended way to have defaults “flow” into `x(t)[…]` without manually building the vector for `ODEProblem`?

Yes, though we should probably throw a better error here @cryptic.ax

> [@rasmuskovacs](#):
>
> - When expressions like `v*k` naturally produce a 1×1 matrix, what’s the idiomatic way to feed a scalar variable on the LHS? Is `(v*k)[1,1]` the intended approach?

Yes it’s intended. See “Taking Vector Transposes Seriously” for details about this whole topic.

[![](https://global.discourse-cdn.com/julialang/original/3X/e/5/e55a2a411b08eba0d6e2cdb7b9392b0f4a8a5eee.jpeg "Taking Vector Transposes Seriously | Jiahao Chen | JuliaCon 2017") ](https://www.youtube.com/watch?v=C2RO34b_oPM)

> [@rasmuskovacs](#):
>
> Is there a best practice for declaring vectors vs 2D “column vectors” in `@variables`/`@parameters`? For example, should “state vectors” generally be `x(t)[1:2]` (1D) rather than `x(t)[1:2,1:1]` (2D)? Any pitfalls to be aware of when mixing 1D and 2D shapes in equations?

It’s the same as in Julia. If you want a 1D object you should prefer using the 1D object. From my understanding our symbolic rules match Julia here, and Jiahao’s talk describes a lot of the reasoning behind it.

---

<div class="post-metadata">

**Author:** ![cryptic.ax](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cryptic.ax/32/220540_2.png) [@cryptic.ax](https://discourse.julialang.org/u/cryptic.ax)\
**Post date:** [October 3, 2025, 11:17am UTC](https://discourse.julialang.org/t/modelingtoolkit-matrix-variables-defaults-1x1-scalars/132825/3 "2025-10-03T11:17:43Z")

</div>

I think we have a case to flow defaults from `[x1, x2]` to `x`, but it’s a heuristic and may not always work. It depends a lot on how the system simplifies.

> Yes, though we should probably throw a better error here

What exactly is the error condition? This should already throw an error saying “no initial conditions for `x`”

---

<div class="post-metadata">

**Author:** ![rasmuskovacs](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rasmuskovacs/32/218844_2.png) [@rasmuskovacs](https://discourse.julialang.org/u/rasmuskovacs)\
**Post date:** [October 3, 2025, 12:17pm UTC](https://discourse.julialang.org/t/modelingtoolkit-matrix-variables-defaults-1x1-scalars/132825/4 "2025-10-03T12:17:37Z")

</div>

I get the following if I don’t manually supply the initial values for x(t)[1:2,1:1]:

ERROR: MethodError: Cannot `convert` an object of type ModelingToolkit.NoValue to an object of type Real  
The function `convert` exists, but no method is defined for this combination of argument types.

Closest candidates are:  
convert(::Type{T}, ::Unitful.Quantity) where T\<:Real  
@ Unitful ~/.julia/packages/Unitful/sxiHA/src/conversion.jl:167  
convert(::Type{T}, ::Number) where T\<:Number  
@ Base number.jl:7  
convert(::Type{T}, ::Unitful.Level) where T\<:Real  
@ Unitful ~/.julia/packages/Unitful/sxiHA/src/logarithm.jl:46  
…

Stacktrace:  
[1] setindex!(A::Memory{Real}, x::ModelingToolkit.NoValue, i1::Int64)  
@ Base ./genericmemory.jl:243  
[2] unsafe\_copyto!(dest::Memory{Real}, doffs::Int64, src::Memory{ModelingToolkit.NoValue}, soffs::Int64, n::Int64)  
@ Base ./genericmemory.jl:153  
[3] unsafe\_copyto!  
@ ./genericmemory.jl:133 [inlined]  
[4] _copyto\_impl!  
@ ./array.jl:308 [inlined]  
[5] copyto!  
@ ./array.jl:294 [inlined]  
[6] copyto!  
@ ./array.jl:319 [inlined]  
[7] copyto\_axcheck!  
@ ./abstractarray.jl:1167 [inlined]  
[8] Matrix{Real}(x::Matrix{ModelingToolkit.NoValue})  
@ Base ./array.jl:626  
[9] convert  
@ ./array.jl:618 [inlined]  
[10] symconvert(::Type{Matrix{Real}}, x::Matrix{ModelingToolkit.NoValue})  
@ ModelingToolkit ~/.julia/packages/ModelingToolkit/hlGut/src/systems/parameter\_buffer.jl:4  
[11] MTKParameters(sys::System, op::Dict{…}; tofloat::Bool, t0::Nothing, substitution\_limit::Int64, floatT::Type, p\_constructor::Function, fast\_path::Bool)  
@ ModelingToolkit ~/.julia/packages/ModelingToolkit/hlGut/src/systems/parameter\_buffer.jl:124  
[12] process\_SciMLProblem(constructor::Type, sys::System, op::Dict{…}; build\_initializeprob::Bool, implicit\_dae::Bool, t::Nothing, guesses::Dict{…}, warn\_initialize\_determined::Bool, initialization\_eqs::Vector{…}, eval\_expression::Bool, eval\_module::Module, fully\_determined::Nothing, check\_initialization\_units::Bool, u0\_eltype::Nothing, tofloat::Bool, u0\_constructor::typeof(identity), p\_constructor::typeof(identity), check\_length::Bool, symbolic\_u0::Bool, warn\_cyclic\_dependency::Bool, circular\_dependency\_max\_cycle\_length::Int64, circular\_dependency\_max\_cycles::Int64, substitution\_limit::Int64, use\_scc::Bool, time\_dependent\_init::Bool, algebraic\_only::Bool, allow\_incomplete::Bool, is\_initializeprob::Bool, kwargs::@Kwargs{…})  
@ ModelingToolkit ~/.julia/packages/ModelingToolkit/hlGut/src/systems/problem\_utils.jl:1445  
[13] (NonlinearLeastSquaresProblem{…})(sys::System, op::Dict{…}; check\_length::Bool, check\_compatibility::Bool, expression::Type, kwargs::@Kwargs{…})  
@ ModelingToolkit ~/.julia/packages/ModelingToolkit/hlGut/src/problems/nonlinearproblem.jl:83  
[14] NonlinearLeastSquaresProblem  
@ ~/.julia/packages/ModelingToolkit/hlGut/src/problems/nonlinearproblem.jl:77 [inlined]  
[15] #_#1044  
@ ./none:0 [inlined]  
[16] InitializationProblem{…}(sys::System, t::Float64, op::Dict{…}; guesses::Dict{…}, check\_length::Bool, warn\_initialize\_determined::Bool, initialization\_eqs::Vector{…}, fully\_determined::Nothing, check\_units::Bool, use\_scc::Bool, allow\_incomplete::Bool, algebraic\_only::Bool, time\_dependent\_init::Bool, kwargs::@Kwargs{…})  
@ ModelingToolkit ~/.julia/packages/ModelingToolkit/hlGut/src/problems/initializationproblem.jl:138  
[17] InitializationProblem  
@ ~/.julia/packages/ModelingToolkit/hlGut/src/problems/initializationproblem.jl:20 [inlined]  
[18] #\_#1098  
@ ./none:0 [inlined]  
[19] maybe\_build\_initialization\_problem(sys::System, iip::Bool, op::Dict{…}, t::Float64, defs::Dict{…}, guesses::Dict{…}, missing\_unknowns::Set{…}; implicit\_dae::Bool, time\_dependent\_init::Bool, u0\_constructor::Function, p\_constructor::Function, floatT::Type, initialization\_eqs::Vector{…}, use\_scc::Bool, kwargs::@Kwargs{…})  
@ ModelingToolkit ~/.julia/packages/ModelingToolkit/hlGut/src/systems/problem\_utils.jl:1118  
[20] process\_SciMLProblem(constructor::Type, sys::System, op::Dict{…}; build\_initializeprob::Bool, implicit\_dae::Bool, t::Float64, guesses::Dict{…}, warn\_initialize\_determined::Bool, initialization\_eqs::Vector{…}, eval\_expression::Bool, eval\_module::Module, fully\_determined::Nothing, check\_initialization\_units::Bool, u0\_eltype::Nothing, tofloat::Bool, u0\_constructor::typeof(identity), p\_constructor::typeof(identity), check\_length::Bool, symbolic\_u0::Bool, warn\_cyclic\_dependency::Bool, circular\_dependency\_max\_cycle\_length::Int64, circular\_dependency\_max\_cycles::Int64, substitution\_limit::Int64, use\_scc::Bool, time\_dependent\_init::Bool, algebraic\_only::Bool, allow\_incomplete::Bool, is\_initializeprob::Bool, kwargs::@Kwargs{…})  
@ ModelingToolkit ~/.julia/packages/ModelingToolkit/hlGut/src/systems/problem\_utils.jl:1382  
[21] (ODEProblem{…})(sys::System, op::Dict{…}, tspan::Tuple{…}; callback::Nothing, check\_length::Bool, eval\_expression::Bool, expression::Type, eval\_module::Module, check\_compatibility::Bool, kwargs::@Kwargs{})  
@ ModelingToolkit ~/.julia/packages/ModelingToolkit/hlGut/src/problems/odeproblem.jl:81  
[22] ODEProblem  
@ ~/.julia/packages/ModelingToolkit/hlGut/src/problems/odeproblem.jl:73 [inlined]  
[23] ODEProblem  
@ ./none:0 [inlined]  
[24] ODEProblem(sys::System, op::Dict{Symbolics.Arr{Num, 2}, Matrix{Float64}}, tspan::Tuple{Float64, Float64})  
@ ModelingToolkit ./none:0  
[25] top-level scope  
@ ~/Documents/Adaptive\_Control/matrix\_sim.jl:26  
Some type information was truncated. Use `show(err)` to see complete types.

And when I saw this  
julia\> defaults(fol)  
Dict{Any, Any} with 27 entries:  
k[1, 1] =\> 1  
(x(t))[2, 1] =\> NoValue()  
(Initial(x(t)))[2, 1] =\> false  
(Initial(xˍt(t)))[1, 1] =\> false  
Initial(uˍt(t)) =\> BasicSymbolic{Real}[(Initial(uˍt(t)))[1, 1];;]  
A1[2, 1] =\> 0  
x1(t) =\> 1  
k[2, 1] =\> 1  
Initial(x(t)) =\> BasicSymbolic{Real}[(Initial(x(t)))[1, 1]; (Initial(x(t)))[2, 1];;]  
Initial(u(t)) =\> BasicSymbolic{Real}[(Initial(u(t)))[1, 1];;]  
(Initial(xˍt(t)))[2, 1] =\> false  
(Initial(uˍt(t)))[1, 1] =\> false  
A1[1, 2] =\> 0  
Initial(x1ˍt(t)) =\> false  
Initial(xˍt(t)) =\> BasicSymbolic{Real}[(Initial(xˍt(t)))[1, 1]; (Initial(xˍt(t)))[2, 1];;]  
Initial(x2(t)) =\> false  
v[1, 2] =\> 2  
v[1, 1] =\> 1  
A1[1, 1] =\> -1  
x2(t) =\> 1  
A1[2, 2] =\> -2  
(x(t))[1, 1] =\> NoValue()  
⋮ =\> ⋮

it made sense to me what was going on with the NoValue(), but it took some lurking around

---

<div class="post-metadata">

**Author:** ![rasmuskovacs](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rasmuskovacs/32/218844_2.png) [@rasmuskovacs](https://discourse.julialang.org/u/rasmuskovacs)\
**Post date:** [October 3, 2025, 12:34pm UTC](https://discourse.julialang.org/t/modelingtoolkit-matrix-variables-defaults-1x1-scalars/132825/5 "2025-10-03T12:34:13Z")

</div>

I see, thanks for the explanation! That makes sense.

But I still would have expected the default values to be mapped automatically, just like with scalars. Wouldn’t that make more sense? I can’t really think of a case where you wouldn’t want the defaults to flow through to `x`.

It feels a bit confusing when you start adding structure, like combining signals into vectors to keep the math tidy. In small examples it’s fine to handle manually, but in bigger systems it could get messy to keep track of all the different variables and their defaults.

Is there a reason why it works this way today, or is it just a limitation in the current implementation?
