Elixir-based subsystem(s) for Monty?

Dashbit is an Elixir-focused consulting firm, led by José Valim (Elixir’s creator). A recent posting by Jonatan Kłosko discusses their historic approach to integrating Python code into Elixir and then presents some new options:

… today we announce Pythonx, which embeds the Python interpreter within the Erlang VM, bringing automatic data conversion between Elixir and Python, code evaluation, and automatic virtual environment management. …

… To enable even more powerful workflows, we started working on Python support in Livebook, building on Pythonx. The idea, though, is not to support Python separately, but rather to allow Elixir and Python interacting in the same notebook! …

… This brings us to Fine, a C++ library enabling more ergonomic NIFs, tailored to Elixir. … Other than extendable encoding/decoding, Fine provides smart pointers to safely manage resource objects and support for raising exceptions anywhere in the NIF. …

Embedding Python in Elixir, it’s Fine

Details, Caveats, etc.

The embedding mentioned above relies on a step through another ecosystem. Specifically, NIFs allow C/C++ code to be brought into the Erlang (and thus Elixir) VM:

Native Implemented Functions (NIFs) are functions implemented in code that compiles natively to the target machine and are executed outside of the confines of the BEAM virtual machine.
Supercharge Your Elixir with Native Implemented Functions (NIFs)

So, given that the CPython interpreter can be loaded as a C/C++ library, we can run it as part of a NIF. The interpreter can then run (relatively) arbitrary Python code.

However, all of this requires some boilerplate code which can get tedious to write (and tricky to debug). In addition, Python’s global interpreter lock can interfere with the BEAM’s ability to handle concurrency.

Fortunately, Pythonx, Fine, and friends may be able to smooth over the rough spots. As the posting indicates, these are all emerging technologies. Still, anyone who is interested in the idea of using Elixir with Monty should definitely skim the posting.

1 Like

the world doesn’t stand still, and as I now see, @Rich_Morin has posted already, that the creator of Elixir announced their Elixir calling Python via CPython library: pythonx

Importantly, the wrapper library also includes dependency management. This opens up opportunities for interesting PoCs. For now, tbp.monty is a monorepo, (which is good, in my view), but not published as a package. Until then, I hope, it’s ok (@vclay ?) to reuse individual modules from tbp.monty (with correct attribution, of course), in a PoC to e.g. demonstrate a potential future use-case.

I’d start with the simplest thing: pure protocol compatibility: creating the a serialized CMP message through the Python code, and not hand-crafted in Elixir, for example.

More elaborate ideas would need a desired architectural direction to be useful. Ideally, in the end, running a real time-based voting experiment would be my goal.

1 Like

As of last week we do have semver packages in anaconda! :tada:

These are brand new so might be a bit bleeding edge :D. Let us know how you get on.

2 Likes

it seems, to be usable, it must be specifiable in pep508 format. I’ve managed to get the dependencies installed on arm64: GitHub - d-led/tbp_monty_via_pythonx
will try to make it work in the next free time slot. @Rich_Morin feel free to fork/pick up.

upd: the code now actually asserts on tbp.monty’s python result from Elixir. Habitat-sim is the biggest blocker. It works on arm64 but not on other platforms the way I used it.

1 Like

fixed on github actions: actual latest tbp.monty code being called from Elixir, based off a unit test for now, nothing fancy. The macos-latest run fixes the bus error trying to do it without the following environment variables:

MKL_NUM_THREADS=1
NUMEXPR_NUM_THREADS=1
OMP_NUM_THREADS=1
1 Like

Most of the obvious reasons for considering Elixir-based subsystem(s) for Monty have to do with the BEAM’s ability to provide robust (e.g., convenient, fail-soft, performant, reliable, scalable, soft real-time) infrastructure for concurrent and/or distributed sets of lightweight processes (i.e., Actors).

That said, Elixir itself has a lot of useful add-ons and features, including:

  • Axon (high-level interface for creating neural network models)
  • Livebook (executable notebook, ala Jupyter)
  • Macros (Lisp-style syntactic macros)
  • Nerves (support for networks of embedded Elixir systems)
  • Nx (numerical Elixir, optimized for CPU and GPU usage)
  • OTP (support for concurrency, distribution, etc.)
  • Phoenix Framework (Elixir-based web framework)
  • Pythonx (Python interpreter embedded in Elixir)

Although all of this seems pretty compelling to me, it’s far from the whole story. In particular, it says very little about ways in which using Elixir can affect the way that programs are written. So, let’s get into it…

One obvious difference between Elixir and Python is that Elixir is based on a (very digestible, IMHO) flavor of functional programming (FP), as opposed to (say) object-oriented programming (OOP).

So, for example, values are immutable, but variables can be reassigned. Along with avoiding race conditions in concurrent systems, this is in line with Jessica Joy Kerr’s pithy observation:

GOTO was evil because we asked, “how did I get to this point of execution?”. Mutability leaves us with, “how did I get to this state?”.

In terms of digestibility, Elixir eschews monads and such, allowing us to stop trying to understand statements like this: “a monad is a monoid in the category of endofunctors”. And, although Elixir (like Python) is a dynamic programming language, it is in the process of incorporating some extremely cool tech: gradual set-theoretic types.

Anyway, while (re)watching some Dave Thomas videos, I came across a couple that might answer @vclay’s request for introductory talks, while presenting interesting conceptual content, historical background, and some ideas that venture near Monty-related topics.

In Stroop, Whorf, And Elixir, Dave provides a gentle introduction to (and arguments for) Elixirish ways of thinking about programming. The video is fairly long, but well worth watching (including the Q/A session at the end).

As a historical update, note that this talk was given about a decade ago. Thus, any questions about Elixir being “ready for prime time” have been successfully resolved (i.e., it’s VERY stable).

More recently, Dave gave an untitled talk at the 2019 Gig City Elixir.
In this talk, he dives even deeper into programming philosophy and practice. A lot of fun, albeit a bit chewy in spots… (FYI, the actual talk material begins here).

Starting with a quote from Yogi Berra (“If the world was perfect, it wouldn’t be.”), Dave explores the notion of using Elixir to do exploratory (and necessarily imperfect) things. This is pretty much my vision for Monty in a nutshell, but Dave goes on to describe an Elixir-based system that has a lot of things in common with Monty, including pattern recognition.

To get there, he wanders through a discussion of Prolog, Erlang, procedural and declarative programming, pattern matching, Datalog, Datomic, tuple spaces, Dedalus, etc. Along the way, he presents this (mock) quote from Zoltar:

For our visually handicapped friends, here is a textual version:

Your Fortune

I predict that in the future, software will be written as small decoupled components that interact over a dynamic and evolving large scale network.

Your lucky color is purple.

This sounds quite a bit like Monty to me, though the details of his notion(s) vary substantially (!). Then again, in a research project, it’s OK (indeed, required) to try out various approaches.

For example, I went down a small rabbit hole regarding Datalog, Dedalus, Graph Databases, etc. I think this could be a useful way to search Monty’s “discoveries”, but getting there would clearly require some Real Work :trade_mark:. FYI, here’s a page that covers some recent work in this area: Dedalus: Datalog in Time and Space

Anyway, I strongly recommend watching both videos (and really, almost anything from Dave Thomas)…

3 Likes

sorry for a long-ish silence. Somewhat unrelated by yet related, I have created yet another demo that would demonstrate several strengths of Elixir and its underlying platform, via Phoenix LiveView

especially, with regard to brains but also technical demos what can be seen:

  • uninterrupted operation of a system in presence of (unexpected) errors, even under computing node failures through redundancy
  • easy, server-side rendering of SVG components - good for the demos
  • clustered and single node operation from the same codebase

what you can see in the screencast is

  • a ball simulated at the server
  • its behavior is changed at run-time (similar and paving a path towards to a hot code upgrade without interruption)
  • a crash of the ball process is triggered
  • a crash of the node in the cluster at which the ball is being simulated is triggered
  • the ball continues to be simulated and animated without interruption as its process is restarted - either on the same or another node

svg-ssr-ball-demo|656 x808
(open in a new tab for a better view)

all this functionality did not require additional infrastructure such as databases or message queues outside the run-time

1 Like

Thanks, @DLed! Here are some thoughts on a (vaguely) related topic…

Use of Phoenix in Monty LMs

I’ve been wondering about using a copy of Phoenix in each Monty LM. Although this might appear to be inefficient, I’m not sure it would be. Because the code itself would be automagically shared by the LMs, the memory footprint wouldn’t go up very much. And, because Phoenix is very actively developed and maintained, its major code paths are likely to be pretty fast.

Getting on to possible benefits, parts of the Phoenix ecosystem (e.g., Ecto, GraphQL, LiveView) could make it much easier to monitor, record, and adjust (sets of) LMs. For example, imagine creating heat maps and other visualizations based on LM activation patterns, using GraphQL to inspect internal LM data structures, etc.

More generally, I think that the Plug infrastructure could greatly ease dispatching and organization of Elixir-based LMs. Because the LM is trying to emulate an entire cortical column, it could have lots of input and output “ports”. Handling all of these in a clear manner could be a challenge; Plug offers a lot of support for this sort of thing…

Finally, a lot of Phoenix is devoted to handling URI-style addresses. So, if Monty adopted these, messages could be routed to relevant LMs and input ports in a flexible and well understood manner.

Nu?

1 Like

Hi Rich, thanks for the thoughts!

In my experience, so many concepts from the Phoenix/Elixir/Erlang/OTP/BEAM world can be overwhelming that for most introductions of technology I’d go for the KISS solution based on YAGNI.

While creating the demo I thought I’ll need a distributed database (e.g. Mnesia) and later opted out for a simpler in-memory pub/sub based approach. Despite all this, it’ll probably take weeks to learn all the concepts that went into the demo from scratch.

At a recent Elixir meetup, Benjamin has demonstrated how to come up with something like Plug from scratch.

Despite all the existing goodies, for any intro of Elixir into the thousand brains ecosystem, I’d choose basic elixir concepts and built-in libraries first (apart maybe from clustering), and refactor as needed.

In that light, an LM for now could simply be a GenServer that communicates to other actors (processes) via pub/sub or GenServer call/cast depending on the use case. I keep your ideas in mind about addressing and routing but perhaps for the first iterations, simple PIDs or registered names would suffice, allowing for explicit topologies that are easy to reason about instead of a reconstruction/mapping e.g via an URI (as a first step). URIs become interesting when crossing technological boundaries in that context.

As for Phoenix and its LiveView, I’d keep them for front-end only for now as well.

1 Like

I understand your reluctance to add complexity (both conceptual and concrete) to the early-stage Elixir work on Monty. I also recognize (and applaud!) the fact that you have been writing Real Code ™, while I’ve just been sitting in the peanut gallery. Finally, I realize that the current state of Monty R&D is tasked with getting single LMs and SMs to recognize behaviors, composite objects, etc.

That said, I’d like to see the Elixir (etc) side of things allow for various approaches in addressing, architecture, etc. I’m not sure how this should play out in the code space, but I think that keeping it in mind as a goal could have important long-term benefits.

1 Like

oh, another learning via just trying out: on huggingface one can actually start a free tier LiveBook and thereby simply try out various features of Elixir and its ecosystem without installing any infrastructure locally.

One of the key difference to Jupyter notebooks is that each cell runs in its own isolated (OTP) process :flexed_biceps:t2:.