Yoga Language Reference
Yoga is purely functional, meaning that every function takes inputs and returns outputs, without any side effects.
Yoga programs have an implicit main loop, which is run every time a hardware sensor reports new values. It then propagates updates through the program like a spreadsheet.
Yoga’s lexical syntax is close enough to JavaScript that you can use JavaScript syntax highlighting in your editor.
Types
Primitive types include R
(a doubleprecision real), bool
, and string
.
Matrix types are written like R[4,4]
.
Structs are a lot like C:
struct GreppyBalanceState {
R tiltbackIntegral;
R vel;
R speedErrFilter;
R steer;
R accel;
}
A onehot structure is like a struct, but the sum of all the terms must either be 0 or 1:
onehot GreppyNavMode {
initializing;
standup;
driveable;
};
Functions
Functions have 3 kinds of parameters: in, out, and update. This is syntactic sugar on top of a purely functional runtime. Consider the following function, which implements a lowpass filter:
Note that an update
parameter refers to different values depending on which side of an assignment it’s on. For example, when s
is an update parameter, s.x = s.x + 1
means something like sNext.x = sPrev.x + 1
.
Being able to provide small parts of the update or output parameters at a time, rather than all at once, is a major advantage when the state is large and complex.
Update parameters allow convenient calling of stateful functions like filters. For example,
struct JointState { ... }
function jointCtl(
out JointCmd cmd,
update JointState s,
in JointSensors sens,
in JointGoal goal,
in JointConfig c)
{
posErr = goal.pos  sens.pos;
velErr = goal.vel  sens.vel;
cmd.torque = lpfilter2(., s.torqueFilter,
c.torqueFilterPeriod, c.torqueFilterQ,
c.torquePosGain * posErr + c.torqueVelGain * velErr);
}
The call to lpfilter2
materializes the state variables necessary for a 2ndorder lowpass filter under s.torqueFilter
, and updates them at every iteration.
Any literal number in yoga can be designated as a parameter by means of the ~
operator.
These parameters become targets of backpropagation.
For example, we can write a robot leg controller like:
cmd.knee = jointCtl(., state.knee, sensors.knee, goals.knee, JointConfig {
torqueFilterQ: 3.7~10,
torqueFilterPeriod: 0.05~0.1,
torquePosGain: 5.5~10,
torqueVelGain: 2.5~10,
});
cmd.hip = jointCtl(., state.hip, sensors.hip, goals.hip, JointConfig {
torqueFilterQ: 3.7~10,
torqueFilterPeriod: 0.08~0.1,
torquePosGain: 8.5~10,
torqueVelGain: 3.5~10,
});
This defines the feedback parameters for two joints. All the parameters are marked with the ~
operator, which means they can be modified by backpropagation, or by manually dragging the parameter slider in the studio UI.
They are modified both in the parameter table used by the simulator, and in the source code. That’s right, dragging a slider in the studio UI will modify the source code on disk.
If statement
In Yoga, both branches of an if statement are computed, and the results are combined according to the if argument. If it’s a bool
, the end result is a lot like languages you’re used to. If it’s an R
in [0,1], the values assigned on each branch are smoothly blended together. (This helps programs be fully differentiable.)
Operators
Every type T
is equipped with several functions that allow them to be used like vectors in a linear algebra:
T operator *(R aCoeff, T a)
: scalar multiplicationT linearComb(R aCoeff, T a, R bCoeff, T B)
: computesaCoeff*a + bCoeff*b
R linearMetric(T a, T b)
: computes a dot product
Matrices
Matrix constructors take arguments in columnmajor order, so
m = R[4,4](1,2,3,0, 5,6,7,0, 9,10,11,0, 0,0,0,1);
Corresponds to:
1 5 9 0
2 6 10 0
3 7 11 0
0 0 0 1
Runtime
A yoga program should specify a runtime block, tell it how to run live on a robot. See an example.
The runtime defines engines corresponding either to a yoga function or a hardware interface.
Performance
Yoga is compiled using the LLVM backend, so it’s pretty fast. Typical numerical computation is often faster than C, because the language has guaranteed aliasing semantics. It’s easily capable of controlling a complex system like a biped robot with a 1 kHz update loop on an Intel NUC.
Operator precedence
() [] > . left to right
!  unary right to left
~ (parameter) left to right
^ (exponentiation) left to right
 (pipe) left to right
* / % left to right
+  left to right
< <= > >= left to right
== != left to right
&& left to right
 left to right
?: right to left
= += = etc. right to left
Function reference
sin(theta)
,cos(theta)
,tan(theta)
min(a, b, ...)
,max(a, b, ...)
clamp(value, lo, hi)
: returns value limited to betweenlo
andhi
abs(x)
,sign(x)
,sqrt(x)
exp(x)
,log(x)
normsq(x)
: squared norm of any value including structs and matrices (treated elementwise)hypot(x)
: square root ofnormsq

sum(x)
: sum of all elements of any value mat44Translation(x,y,z)
: returns a R[4,4] translation matrixmat44RotationX(theta)
: returns an R[4,4] rotation matrix around X (alsoY
,Z
)fromHomo
: convert an R[4] to an R[3] by dividing by last coordinate
matToHom
: convert an R[3,3] to an homogeneous R[4,4] lpfilter1(out R output, update R state, in R period, in R input)
: single pole lowpass filterlpfilter2(out R output, update Filter2State state, in R period, in R input)
: twopolw lowpass filtereaseIn(out R output, in R phase)
: raised cosine transition function (alsoeaseOut
)