Python porting to Julia resources

I’m trying to port an Machine Learning app that was written in Python using Pytorch to Julia and I’m a newbie in Julia.
Is there a resource,cheat sheet that show how the same thing work in Julia for examples
Python Julia
class ?
init ?
call ?
iter ?
**kwargs ?
etc…
I’ve been reading Julia doc but would take too long I want to get this done a little faster

1 Like

Are you asking for help woth those specific things or a resource for the machine learning functions?

  • Classes don’t exist in Julia
  • Init isn’t necessary to create structures. Loook at Constructors · The Julia Language
  • Iterators are pretty broad and what you want to do will depend on the type of iteration.
  • You can just write methodname(; kwargs...)
1 Like

If you intend to actually use Julia besides porting one application, you’ll probably be better off learning Julia properly. The documentation and several available tutorials are available for that. It quickly pays off when you don’t have to consult online forums for simple things.

10 Likes

I thought some one have done this before porting their Python app over to Julia
they might have some resource that they used willing to share on the net.

1 Like

I’m sure similar things have been ported over but it’s unclear what you would need. There’s not a general “How to convert Python to Julia” guide (although I’m sure there’s some sort of numpy to julia guide out there).

There’s not an exact PyTorch equivalent, but there are several neural net libraries. You can get a lot done with Flux.jl. I’m not personally familiar with Knet.jl, but I’ve heard it’s well implemented and easy to use. You could also use Tensorflow.jl which you might prefer if you’re familiar with Tensorflow but want the benefits of Julia.

Those libraries will give you a good idea of how to port standard layers that you’re probably use to in other ML libraries, but Julia takes an entirely different approach than the Python mega libraries out there. With Julia you can have many libraries be interoperable but still be fast. In Python each library has to reinvent a lot of stuff for their own use. In other words, you will likely need lots of tools that are much less involved than learning all of PyTorch.

1 Like

Thank for the suggestion.

No problem. Feel free to ask more questions here. The Julia community is very friendly to newcomers.

1 Like

You may find this useful: Noteworthy Differences from other Languages · The Julia Language

1 Like

I have recently worked through porting Lark (a Python parsing package) to Julia. Some little things to be prepared for:

  • A single character ‘a’ and a single character string “a” are different in Julia, unlike Python. Equality tests in Julia will therefore fail where they succeed in Python. In general, make sure you know when a Julia function returns a character. You can add a method in Julia to make them equal, of course.
  • Do you know how the syntax a,=b works in Python? In Julia? Check it out!
  • Make sure you know the difference between push!, append! in Julia and Python’s append, extend, +=. Hint: append works differently.
  • Python code loves things like if b:, where None and empty both count as False. nothing and an empty string/list/tuple are not false in Julia.
  • Make sure that both Julia hash and == behave the same way as Python for your complex structures. I spent the most debugging time discovering that functions like unique!, Set(), Dict() had slightly different ideas as to the contents of lists and sets than I had.
  • The Julia “join” function takes the joining string as the final argument, not the first.

To answer your original question, I do not know of a cheatsheet, and I suspect each Python codebase will be approached differently depending on the particular coding philosophy.

The strategy I took with __init__ was to define one or more outer constructors for that type.

**kwargs are handled by “splatting”" and “slurping”.

The only use of __call__ in my source codebase appeared to be a way of passing around a closure in an object…so I just created a closure in Julia.

I didn’t have to work with __iter__. Python generator functions (containing yield) can be modeled using unbuffered Julia Channels. But beware: as Python is single-threaded, the code after the yield is not executed until whichever code was yielded to calls back into the generator. However, in Julia code following a put! is executed as soon as the value is read, and runs right up until the next put!. So, if the Python code calling the generator sees fit to mutate any structures that the generator is using after it has received the value but before it calls for the next value, then the Julia code may diverge in behaviour, as it will be running in parallel with the generator, unlike the Python code. The way I solved this was to have a two-way channel so that the calling function had to send a dummy value back to allow calculations to proceed.

None of this is to imply that it might not be better to restructure the Julia code to use more natural Julia structures. As this codebase was not my own, I didn’t feel confident in getting too fancy until I had all the tests passing.

9 Likes

Thank, this is what I’m looking for you should put this on cheat sheet or web site,so those of us that come after you don’t have to spend hour debugging them to find out that it works a little differently. May be later when I have more time and better understanding of Julia I’ll rewrite it in Julia

Yes, It’s been helpful, thank.

I am in about the same position as @vphom - I just started to learn Julia, and I have a small application or two, which I would probably like to port from Python and further develop it in Julia. “Probably” in my case because whether we will really need the applications depends on many circumstances.

So in one case I started with the Python application and tried to port the most computational expensive part of it to Julia, than call it from Python through pyjulia. That actually worked on one of the computers, while on another one I couldn’t properly install it. Thus I could port the module function by function and check results against the original program.

To the list of the things to be prepared for (@frtps , thank you for your post), I would like to add one more - the scoping rules as discussed here: Dicussion on scoping rules . The nasty thing is that in many cases your try blocks and loops would work in Julia just as in Python - because you just happend to initialize the corresponding variable in some way.

As a general remark - from my recent experience: Julia may look familiar on the first glance, but pretty soon you will find out you need to invest time in reading at least a big part of the manual anyway.

Also:

1 Like

I recently wrote two libraries with the same functionalities in Julia and Python.
In both version, I tried to make them as Julian and Pythonist as I could.
This includes some design considerations like inheritance (normal in Python) vs composition (normal in Julia), as well as class design and the use of macros in Julia.
There are also some differences in python vs julia syntax, like you mentioned: init, call, iter, kwargs.

Hopefully it can help. Here are the links:
Julia version: GitHub - rizalzaf/AdversarialPrediction.jl: Easily optimize generic performance metrics in differentiable learning.
Python version: GitHub - rizalzaf/ap_perf: Easily optimize generic performance metrics in differentiable learning.

I also realized that those projects may not be the best way to write code in the best Julian and Pythonist way. Let me know if there is any suggestions.

1 Like

A short comparison of the structure / concepts between both languages, as far as I understood, maybe this is helpful for you. Please correct me if I am wrong.

Python name Python meaning Julia name Julia meaning
def f(x,y): named function - duck-typing, no polymorphism fuction f(x,y) duck-typing, multiple dispatch to methods
lambda x: x anonymous function (x) -> x anonymous function
class MyClass(Parent) classical OOP class, multiple inheritance struct MyStruct{TypeParameters} <: AbstractParent data and constructor only, types inside struct can be parametrized, can inherit only from abstract types
method function that belongs to a class (single dispatch on first argument self) method function implementation for concete parameter types (multiple dispatch on all arguments)
def __init__(self, x): constructor of a class, one per class MyStruct(x) Constructor method (multiple constructors can be defined, selected using multiple dispatch)
module namespace - defined as a single .py file module namespace - defined independenly of file strucutre
package directory with __init__.py file NA directory structure independent of logical structure
package bundled and installable, e.g. using pip, conda package includes dependencies (Project.toml), installable using Pkg, very light-weight
@decorator applies a higher order function to the following def, often used for wrappers @macro transformation which has Julia expressions (code) as input and output (very powerful)
with block context manager, used for opening and teardown of resources do block content of the block is passed as anonymous function to function before do statement
raise Exception raising Exceptions throw(Exception) raising Exceptions
try - except - finally Exception handling, can be used as part of normal program workflow try - catch - finally Exception handling, should not be part of normal program workflow due to performance reasons
6 Likes

def init(self, x):

new seems to be comparable.

It would be great if the results of this discussion were used to update

https://docs.julialang.org/en/latest/manual/noteworthy-differences/#Noteworthy-differences-from-Python-1

4 Likes

I found this great comparison of Julia, R, and Python syntax when poking around the Learning section of the JuliaLang homepage.

3 Likes

I support the idea.
Here are some additional differences I could think of.

  • Julia requires end for indexing until the last element. x[1:] in Python is equivalent with x[2:end] in Julia.
  • Julia’s range indexing has the format of x[start:step:stop], whereas Python’s format is x[start:stop:step]. Hence, x[0:10:2] in Python is equivalent to x[1:2:10] in Julia. Similarly, x[::-1] in Python, which refers to the reversed array, is equivalent to x[end:-1:1] in Julia.
  • In Julia, indexing a matrix with arrays like X[[1,2], [1,3]] refers to a sub-matrix that contains the intersections of the first and second rows with the first and third columns. In Python, X[[1,2], [1,3]] refers to a vector that contains the values of cell [1,1] and [2,3] in the matrix. X[[1,2], [1,3]] in Julia is equivalent with X[np.ix_([0,1],[0,2])] in Python. X[[0,1], [0,2]] in Python is equivalent with X[[CartesianIndex(1,1), CartesianIndex(2,3)]] in Julia.
  • Julia’s max and min are the equivalent of np.maximum and np.minimum respectively in Python. While maximum and minimum replace np.max and np.min in Python.
  • The imaginary unit sqrt(-1) is represented in Julia as im, not j as in Python.
  • In Julia, the exponentiation operator is ^, not ** as in Python.
  • In Julia, we use nothing of type Nothing to represent null value, whereas Python uses None of type NoneType.
  • In Julia, the standard operators over a matrix type are matrix operations, whereas, in Python, the standard operators are element-wise operations. When both A and B are matrices, A * B in Julia performs matrix multiplication, not element-wise multiplication as in Python. A * B in Julia is equivalent with A @ B in Python, whereas A * B in Python is equivalent with A .* B in Julia.
  • The transpose operator ' in Julia returns an adjoint of a vector (a lazy representation of row vector), whereas the transpose operator .T over a vector in Python returns the original vector (non-op).
4 Likes

Good idea!
Let’s continue to collect and then make a PR.
How should we struture it best? Maybe one table for syntax (e.g. range vs. 1:10), and one for structuring, etc. (like functions, modules)?