Skip to content

Contributing to the web runtime

Katherine Ye edited this page Jun 11, 2021 · 26 revisions

THIS PAGE IS OUT OF DATE

Optimizers

Speed: Note that the optimizer may take multiple optimization steps per display step (currently 10,000).

  • A "display step" is what happens when you click "step" in the frontend and you see the diagram update. It lets you see more of the process, at the cost of speed.
  • An "optimization step" is one iterative update to the state of the diagram via a descent method.

Writing objectives/constraints/computations in general

  • You will have to use a differentiable number type and differentiable operations in all objectives/constraints/computations.
  • The parameters to these functions are any kind of expression (Expr in types/types.d.ts) or, as you see in the existing examples in the file, a shape. If you see something like [t1, s1]: [string, any], that's a shape input, where t1 is the name of the shape ("Text", etc.) and s1 is the shape information. You can access the parameter of the shape by doing shapeName.property.contents, which will return a VarAD (differentiable floating point number) containing a single number.
    • For example, if you had a circle c as input, and you want its radius, doing c.r.contents will give you something like 5.0 (wrapped in a VarAD).
  • Read these tips on debugging optimization (some may not apply in the new system, but most are helpful)

Writing an objective

  • Add another one to objDict in Constraints.ts.
  • It should output the "badness" of the inputs (as a number or Tensor), and have local minima where you want the solution to be.

Writing a constraint

  • Add another one to constrDict in Constraints.ts.
  • Let's say I want the constraint f(x) <= c to be true.
  • I translate it to the zero-based inequality f(x) - c <= 0.
  • I translate the inequality constraint into an energy (penalty) E(x) = f(x) - c — it is greater than 0 iff the constraint is violated, and the more the constraint is violated, the higher the energy is (e.g. if f(x) is way bigger than c then the energy is a lot bigger than 0). That is the form of all the constraints in the system (and the form you'll be writing them in). It should return a number or Tensor.
    • You may need to multiply it by some weight d depending on how it compares to the overall magnitude of other constraints/energies in the system. E(x) = d * (f(x) - c)
  • The Penrose runtime automatically substitutes each individual constraint into the penalty function p(x) = max(x, 0)^2. This has the effect of ignoring any energy value for a constraint that is satisfied (e.g. if f(x) <= c then E(x) < 0 then you can ignore it), and of making the penalty quadratic for any constraint that's violated.
    • The choice of exponent is arbitrary though squaring seems to work well in practice, as detailed in the notes on exterior point method. Just be careful with how the squaring changes the asymptotic behavior of how you defined the energy originally, e.g. squaring a linear energy works well, but squaring a quadratic may not.
  • The Penrose runtime increases a separate weight on all the constraints (multiplicatively by a factor of 10) for each unconstrained optimization (this is also part of the exterior point method, and you don't have to worry about it, though you should know it's happening).

TODO: give a concrete example here

Writing a computation

  • If you just want to call it from inside the system (e.g. a helper function for an objective or computation), add it somewhere in Functions.ts
  • The only types a computation can take and return are defined in types/types.d.ts, the type Expr (and its branches).
  • You'll have to tag the result of computation with the right type.

Debugging

  • Read these tips on debugging optimization (some may not apply in the new system, but most are helpful)
  • Use the web inspector, as described in the README
Clone this wiki locally