A mixer searching and scheduling experiment using equality saturation, more specifically egg.
This repo can be built with cargo or using the nix flake.
Install rust toolchain manager (rustup):
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | shBuild with cargo:
cargo build --releaseThis repo has a nix flake which can be used to either get a dev environment for working on this repoo or building the project.
To install nix and enable experimental flake feature:
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- installAssuming nix is installed, building the binary:
nix buildGetting a dev environment to work on this repo:
nix developExample code comes with a CLI:
fluido --target-concentration 0.01 --input-space 0.04 --input-space 0 --time-limit 1The command above will try to find output concentration of 0.01 using the provided input concentrations.
Output for the above example:
> fluido --target-concentration 0.01 --input-space 0.04 --input-space 0 --time-limit 1
Starting to equality saturation, this will take ~60 seconds
Runner report
=============
Stop reason: TimeLimit(60.058731875)
Iterations: 655
Egraph size: 813996 nodes, 1276 classes, 813996 memo
Rebuilds: 0
Total time: 60.064849359999975
Search: (0.08) 4.624326999
Apply: (0.90) 54.213128551000004
Rebuild: (0.02) 1.2272947920000008
Optimized sequence: (mix 0 (mix 0 0.04))
Cost: 0.0- DOT output of the produced mixer's computation graph can be seen with
--show-dotflag. - flat-ir of the mixlang produced can be seen with
--show-irflag. - liveness analysis over flat-ir can be seen with
--show-livenessflag.
The saturation starts with a number, the target concentration, for the given example command above:
(0.01)A rewrite rule that expands a number is applied to the target concentation.
rw!("expand-num";
"(?a)" => "(mix ?a ?a)"),The expression becomes:
(mix 0.01 0.01)There is also a rewrite rule for searching different mixer input combinations that would give the same output concentration:
rw!("differentiate-mixer";
"(mix ?a ?b)" => "(mix (- ?a 0.001) (+ ?b 0.001))"),
rw!("differentiate-mixer2";
"(mix ?a ?b)" => "(mix (+ ?a 0.001) (- ?b 0.001))"),So equality saturation is able to find:
(mix 0.0, 0.02)and saturation continues like this.
- One thing to note here is the
Concentrationrepresentation used for the saturation. To be able to control the search space, a special representation for concentrations are used rather than a 'normal'f64. This allows the control over precision. Currently theConcentration::EPSILON=0.0001. Which means0.00002and0.00004are essentially the same concentation for the purpose of saturation.
egg is a saturation tool, which does not have a 'explicit' notion of arithmetics. To be able to do arithmetic reasoning, we defined a MixerLang with some primitive operations such as Add (+), Sub (-). Once a mixer expression become something like (mix (- ?a 0.001) (+ ?b 0.001)) it is merged with the initial expression (mix ?a ?b) as they are equivalent (the former derived from the latter by the differentiate-mixer rewrite rule). Once this merge operation happens, our analysis implementation is called. In which we evaluate the expression and add a Num node equivalent to this node representing the arithmetic operation. This way the results of arithmetic operations can be found and added into the egraph.
MixLang is the intermediate language defined for representing mixer graphs and operations. Currently it consists of following constructs:
| Name | Operand count | Operand type(s) | Explanation | Symbolic |
|---|---|---|---|---|
| Num | 1 | Constant | A consntant number, representing a specific concentration | elem1 |
| Add | 2 | ID, ID | Addition of two e-nodes that are num (or equivalent to a num) | elem1 + elem2 |
| Sub | 2 | ID, ID | Substraction of two e-nodes that are num (or equivalent to a num) | elem1 - elem2 |
| Mix | 2 | ID, ID | Mixing of two e-nodes that are num (or equivalent to a num) | (elem1 + elem2) / 2 |