A small DSL for programming sppl across PyCall.
Author femtomc
3 Stars
Updated Last
2 Years Ago
Started In
October 2020

A small DSL for programming sppl across PyCall.jl.

Example usage

Allows the usage of direct string macros:

spn = sppl"""
Nationality   ~= choice({'India': 0.5, 'USA': 0.5})
if (Nationality == 'India'):
    Perfect       ~= bernoulli(p=0.10)
    if (Perfect == 1):  
        GPA ~= atomic(loc=10)
        GPA ~= uniform(loc=0, scale=10)
    Perfect       ~= bernoulli(p=0.15)
    if (Perfect == 1):  
        GPA ~= atomic(loc=4)
        GPA ~= uniform(loc=0, scale=4)
PyObject <sppl.spn.SumSPN object at 0x7f306382fd30>

as well as the usage of a native macro with native structures:

spn = @sppl begin
    nationality ~ SPPL.Choice([:India => 0.5, :USA => 0.5])
    perfect ~ SPPL.Bernoulli(0.1)
    gpa ~ SPPL.Atomic(4)
PyObject <sppl.spn.ProductSPN object at 0x7f306381f820>

Of course, you can use native abstractions:

@sppl function foo(x::Float64)
    nationality ~ SPPL.Choice([:India => x, :USA => 0.5])
    perfect ~ SPPL.Bernoulli(0.1)
    gpa ~ SPPL.Atomic(4)

which expands to produce a generator:

:(function foo(x::Float64)
      gpa = Main.IndianGPA.SPPL.Id(:gpa)
      nationality = Main.IndianGPA.SPPL.Id(:nationality)
      perfect = Main.IndianGPA.SPPL.Id(:perfect)
      command = Main.IndianGPA.SPPL.Sequence(Main.IndianGPA.SPPL.Sample(nationality, SPPL.Choice([:India => x, :USA => 0.5])), Main.IndianGPA.SPPL.Sample(perfect, SPPL.Bernoulli(0.1)), Main.IndianGPA.SPPL.Sample(gpa, SPPL.Atomic(4)))
      model = command.interpret()
      namespace = (nationality = Main.IndianGPA.SPPL.Id(:nationality), perfect = Main.IndianGPA.SPPL.Id(:perfect), gpa = Main.IndianGPA.SPPL.Id(:gpa), model = model)


There are a few special pieces of syntax which the user should keep in mind. Some of these points make the macro parsing unambiguous, others are more for convenience.

  • Sample statements are expressed using just ~.
  • Transform expressions (a polynomial for example, expressed in Python as X[1] ~ 8 * W[2]**2 + 5) are specified using the "special" operator .>.
  • The Julia ternary expression foo ? b1 : b2 is allowed - this desugars into IfElse.
  • Array declarations are performed using the library-provided array function interface. Array declarations must be made before indexing/use - or else macro parsing will return an error.
  • == desugars to << on the Python side (this creates an event - a condition).
  • The for expression is allowed - but you are restricted to only supply UnitRange{Int64} instances for the parsing/semantics to work properly.

Examples of each of these points can be found in the examples directory. These examples come directly from the sppl Jupyter notebooks. If you'd like to help port these over, just setup a PR!

Used By Packages

No packages found.