Using Matlab from Julia

I am testing the package MATLAB.jl.

This works:

using MATLAB
mat"1+1"

This doesn’t work:

julia> mat"tf([1],[1,1])"
ERROR: ArgumentError: Unsupported kind of variable.
Stacktrace:
 [1] jvalue(mx::MxArray)
   @ MATLAB C:\Users\uwefechner\.julia\packages\MATLAB\xBhSu\src\mxarray.jl:650
 [2] get_variable(name::Symbol)
   @ MATLAB C:\Users\uwefechner\.julia\packages\MATLAB\xBhSu\src\engine.jl:170
 [3] top-level scope
   @ C:\Users\uwefechner\.julia\packages\MATLAB\xBhSu\src\matstr.jl:169

This command is working in Matlab directly, though.

Testing on Windows, Matlab 2021a, Julia 1.9.3.

Any idea?

It seems the return value is of a MATLAB type that MATLAB.jl does not support (it only supports the basics), and I don’t recall what those types might be.

It’s been a while since I used it, and I no longer have MATLAB, but you can use all of MATLAB at the MATLAB side, including all the types, but not send all the types back and forth.

another plausible (no longer believe that) I had copied while lookup of for you:

If the MATLAB function is not in the current directory, we need to first add it to the MATLAB path before calling through Julia:
mat"addpath(‘/path/to/folder’)"

I recall type troubles, and what you could plausible do is make a wrapper function at MATLAB, that converts to appropriate type/format or summarizes somehow, if enough and returns just that (type).

FYI: you can also call Octave, the MATLAB clone. I did both. I used that package and MATLAB, but not for Octave.

If I recall, if you use MATLAB, the license disallows using it to help migrate code…

I was porting/migrating MATLAB code from MATLAB to Julia. They can’t dictate how Octave, and your own MATLAB source code is used, with it.

The Julia project to call Octave was immature, at least at the time, but the one to call from Python and to Octave wasn’t (not Julia default to many BLAS threads, I had to disable that, not because of the Julia side, but because of Octave which didn’t support, with some ENV var). Nor is to call Python, so to it, and to Octave (to run MATLAB code)…

There’s also a MATLAB to Julia transpiler/compiler available. It worked, sort of, not for all syntax, helped with some. Not all the semantics are the same either. If you use MATLAB well, it might not be very valuable. And toolboxes are a problem. I recommend calling, i.e. reusing code if it works for you, in Octave, rather that porting. If it’s missing toolboxes then a problem and pros and cons to reusing vs. porting.

2 Likes

According to the Matlab documentation (Transfer function model - MATLAB) the tf(.) function returns a model object. That sounds like a rather complex (in the sense of non-trivial) type.

So, my first thought is that it’s not that surprising that Julia doesn’t know what to do with it?

1 Like

Right, it’s:

tf model object | genss model object | uss model object

and I’m guessing those are objects, i.e. of a class; MATLAB is object oriented by now, as many languages even COBOL and Fortran. MATLAB.jl doesn’t support objects, maybe it would not be too hard to add some capability to get back somehow? If not there, then you use its “getters” to get data out…

I suppose MATLAB.jl shouldn’t get the data from the object, that would be one way, and break encapsulation, rather just give you an object pointer, likely rather trivial. And then some way to access methods of that object, less trivial but also then needed. So a distributed objects…

[Reminds of of the intriguing (distributed) Unison language, and the great StrangeLoop talk on it I was just watching on Youtube (the AST, not text, and hashing of it was the intriguing part, for immutability/distributed reasons).]

Well, it could just return the string representation… That should be trivial, shouldn’t it?

I thought that the way I passed the input parameters would be wrong. Good to hear that this is not the problem.

In the end, the Julia ControlSystems.jl library has the same sort of object, so in the ideal case it should be converted to that. Not difficult, just a call to the tf function with two parameters, the nominator and the denominator vector… (or array?)

I am pretty happy with my current wrapper for the matlab function simplify:
(This is the only function I found so far that is much more efficient in Matlab than in Julia.)

using ModelingToolkit, MATLAB
@variables t ω Pg Pge Pgc Uest
@variables R a b c d Γ ρ U J Q Qest V A Ku Γest λ λest λnom s Kp Ki Cs

function matlab2julia(str)
     res = replace(str, "rho" => "ρ")
     res = replace(res, "Gamma" => "Γ")
     res = replace(res, "omega" => "ω")
     res = replace(res, "lambda_nom" => "λnom")
     eval(Meta.parse(res))
end
function julia2matlab(str)
     res = replace(str, "ρ" =>"rho")
     res = replace(res, "Γ" => "Gamma")
     res = replace(res, "ω" => "omega")
     res = replace(res, "λnom" => "lambda_nom")
     res = replace(res, "1.5A" => "1.5*A")
     res = replace(res, "0.5A" => "0.5*A")
end

function simplify_m(expr::Num)
     mat"syms t omega Pg Pge Pgc Uest"
     mat"syms R a b c d Gamma rho U J Q Qest V A Ku lambda_nom s Kp Ki Cs"
     sexpr = repr(expr)
     orig = length(sexpr)
     sexpr = julia2matlab(sexpr)
     mat"expr=str2sym($sexpr);"
     mat"expr=simplify(expr);"
     res = mat"convertContainedStringsToChars(string(expr))"
     res = matlab2julia(res)
     println("Simplified expression from $orig to $(length(repr(res))) characters!")
     res
end

expr = (Q - Qest)*(((1.5A*(U^2)*b*Γ*ρ + A*R*U*a*Γ*ρ*ω)*(s + Ku*V)*s) / (J*((Kp*Ku*(Q - Qest)*s*λnom))))
sexpr = simplify_m(expr)

Question: Any idea how to write this in a more generic way:

 res = replace(res, "1.5A" => "1.5*A")
 res = replace(res, "0.5A" => "0.5*A")
...

Matlab needs the “*” for multiplication, but Julia is not providing it. So it would be nice to have a function that inserts it everywhere where it is needed…

As a first step you could collapse the replace calls into a single one, e.g.:

julia2matlab(str) = replace(
    str,
    "ρ" =>"rho",
    "Γ" => "Gamma",
    #= ... =#
)

If I understand you correctly, by “more generic” you mean something like:

replace("some code with 1.5A;", r"(\d+\.\d+)A" => s"\1*A")
# "some code with 1.5*A;"

See ?@s_str and ?replace (and note that “Support for multiple patterns requires version 1.7.”).

Of course there are many cases where this kind of text-to-text code translation will fail.

I got already quite far, but now I am stuck with replacing 2( with 2*( using regular expressions:

using Symbolics

@variables R a b c d Γ ρ U J Q Qest V A Ku Γest λ λest λnom s Kp Ki Cs

input1 = (2U + 0.5U*b + 4.0U*c)
input2 = (2A + 0.5A*b + 4.0A*c + 2(a + b))

function julia2matlab(str)
    res = str
    for symbol in ["A", "U"]
        res = replace(res, Regex("(\\d+\\.\\d+)$symbol") =>  SubstitutionString("\\1*$symbol"))
        res = replace(res, Regex("(\\d+)$symbol") => SubstitutionString("\\1*$symbol"))
    end
    # res = replace(res, Regex("(\\d+)\\(") => SubstitutionString("\\1*\\("))
    res
end

mstring = julia2matlab(repr(input1))
println(mstring)
@assert mstring == "2*U + 0.5*U*b + 4.0*U*c"
mstring = julia2matlab(repr(input2))
println(mstring)
@assert mstring == "2*A + 2*(a + b) + 0.5*A*b + 4.0*A*c"

The commented line fails. What am I doing wrong?

If I do:

res = replace(res, r("(\d+)\(") => s("\1*\("))

I get the error:

julia> include("mwes/mwe19.jl")
ERROR: LoadError: syntax: invalid escape sequence
Stacktrace:
 [1] top-level scope
   @ ~/repos/WindTurbines/mwes/mwe19.jl:14
 [2] include(fname::String)
   @ Base.MainInclude ./client.jl:478
 [3] top-level scope
   @ REPL[37]:1
in expression starting at /home/ufechner/repos/WindTurbines/mwes/mwe19.jl:14

The immediate problem is that you can’t add parentheses around a string macro. r"..." provides special parsing whereas r("...") does normal string parsing and then passes the string to a function r (which you probably hadn’t defined).

Maybe this helps:

julia> replace("2(", r"(\d+)\(" => s"\1*(")
"2*("
1 Like

Thanks, this helped. So I need to escape the bracket when using an r"", but not when using s"" , correct?

I don’t have more insights than “yes, it looks like that”. But it seems reasonable to me that a regex needs more syntactical constructs and thus more escaping than a substitution string.

1 Like