Skip to content
Randall O'Reilly edited this page Nov 19, 2020 · 3 revisions

Simulations can be written in Python instead of Go, for users who are more comfortable with that language.

When using Go, you write a simulation as a main package that generates an executable program, which runs your simulation, compiled from the Go code that configures everything, including the GUI, to run your model.

When using Python, you first compile the entire emergent library of Go code into a new Python executable, which functions just like the standard python executable (which itself is actually just a very small program that links in the python library). Then the specific simulation is .py python code that is run within this new python executable. For the leabra algorithm, this new executable is called pyleabra and it is built in the emer/leabra/python directory, by typing make there (please do read the README.md file there for all the relevant info). This takes much longer than building the Go programs. But once done, this same executable can be used to run any Python-based simulation, without further recompiling. Unless you need something that is not already in the emergent Go library, and must be added or changed in that library, in which case a recompile is needed.

Here are some of the tradeoffs for the Go vs. Python models:

  • Go is faster to build overall, and provides much better compile-time and run-time error messages, and is easier to debug using a standard Go IDE / debugger. Python's initial compile time is much longer, but once that's over, it is fast to load any given new model, so if you're just using the existing standard library, this is not a factor. However, it can sometimes be difficult to debug issues that involve interactions between Python and Go. Also, because Python has no type system, most errors only show up at run time, so you really have to test everything carefully to find any bugs, whereas the Go compiler catches many basic errors.

  • Go runs much faster than Python. However, for many situations, the difference is not too noticeable, because Go is usually always running all the "inner loops" for Python, so that the slower runtime of Python is only affecting the broad outer-loop processing. However, if you find you need to do something down at the Neuron or Synapse level in running the model, then Python can become noticeably slower.

  • Python is much better for interactive data analysis and on-the-fly interactive coding, using Jupyter notebooks or similar such technologies. It may be best overall to run the models in Go, and then just load and analyze the resulting data in Python separately.

  • Go is simpler and cleaner than Python, and thus it is relatively easy to directly translate Go -> Python, but not the other way around. We have a tool, gotopy, which does this Go -> Python conversion, and the resulting .py file requires relatively few fixes to then be runnable. See the leabra gotopy directory for an optimized tool specifically for converting leabra-based models. Thus, if you already know Python, it is not all that difficult to learn Go, so you might want to give that a try as well.

Examples

All the examples in leabra/examples have corresponding .py files, as do all the CCN textbook simulations. These provide the best starting point for your own models, just as is the case for the .go versions.

See the eTorch package for how to run PyTorch neural networks using the emergent NetView GUI -- you can also use Python to integrate PyTorch and Leabra models for example. The examples there include the AlexNet DCNN vision model, which is fun to actually be able to see running.

etable

See etable pyet for example code for converting between the Go etable.Table and numpy, torch, and pandas table structures. All of the converted projects rely on etable because it provides a complete GUI interface for viewing and manipulating the data, but it is easy to convert any of these tables into Python-native formats (and copy back-and-forth). The pyet python library has the necessary routines.

Key Tips

  • Overall, try to maintain a mental model where there is underlying Go code and objects, and the Python objects are "pointers" into those underlying Go objects. For example, there is a leabra.Layer in Python, which is just a "wrapper" around the underlying leabra.Layer in Go. Python uses handles (int64) to refer to the underlying Go objects, and sometimes the Go code may return a handle, in which case you may need to initialize a new Python wrapper using it. For example, this code in ra25.py:
def TestItemCB2(recv, send, sig, data):
    win = gi.Window(handle=recv)

initializes the win wrapper for the recv argument, which is a generic handle in this case.

  • Go slices and Python lists can often interoperate (and Python syntax can be used to access the Go slice just like a Python list), but when calling a Go function that takes a slice argument, you need to pass the Go slice instead of a Python list. In this case, you can initialize a new slice like this:
ss.LayStatNms  = go.Slice_string(["Hidden1", "Hidden2", "Output"])

you can then iterate over this slice using standard python syntax:

        for lnm in ss.LayStatNms:
            ly = leabra.Layer(ss.Net.LayerByName(lnm))

note that we're "casting" the type returned by ss.Net.LayerByName to leabra.Layer because even though Python doesn't care about types, Go still does, and so we need to establish these types to keep Go working properly.

  • Use go.nil to pass a Go nil for functions where a nil can be used:
        sch.append(etable.Column("Run", etensor.INT64, go.nil, go.nil))

However, when testing if a pointer is nil, you actually have to compare to 0 -- go.nil does not work in this case.

        if ss.TrnEpcPlot != 0:
            ss.TrnEpcPlot.GoUpdate()