LooPyGen
LooPyGen copied to clipboard
Variation conditionals
Examples:
"conditions": { // Variation-level condition
"Black": {"include_when": {"Colour": "Green"}}, // "Black" variation for this layer is included if the "Colour" layer variation is Green
"Blue": {"exclude_when": {"Colour": "Orange"}}, // "Blue" variation for this layer is excluded if the "Colour" layer variation is Orange
"Brown": {"exclude_when": { NOT: {"Colour": "Blue"}}}, // "Brown" variation for this layer is excluded if the "Colour" variation is NOT Blue
"Green": {"include_when": { AND: {"Colour": "Yellow"}, {"Hat": "Beret"}}}, // "Green" variation for this layer is included if Colour is Yellow AND Hat is Beret
"Green": {"exclude_when": { OR: {"Colour": "Yellow"}, {"Hat": "Cowboy"}}}, // "Green" variation for this layer is excluded if Colour is Yellow OR Hat is Cowboy
}
A few things need to come together for this feature:
- [x] More efficient random sampling algorithm (without replacement): dev/faster-random-sampling
- [ ] Efficient algorithm to exclude invalid variation combinations from the random sample pool
Because they violate one or more layer-level or variation-level conditionals
- Ideally without replacement (never randomly pick invalid combinations)
- Or with a single replacement (randomly pick combination, check that it is invalid, never pick again)
Such an algorithm is much simpler to implement if a condition on layer
i
can only dependent on layersj < i
- [ ] Parsing of the
conditions
layer key into boolean expressions - [ ] In-UI conditions "builder"
Some minor corrections to the JSON format:
"conditions": { // Variation-level condition
"Black": { "include_when": { "Colour": "Green"} }, // "Black" variation for this layer is included if the "Colour" layer variation is Green
"Blue": { "exclude_when": { "Colour": "Orange" } }, // "Blue" variation for this layer is excluded if the "Colour" layer variation is Orange
"Brown": { "exclude_when": { "NOT": { "Colour": "Blue" } } }, // "Brown" variation for this layer is excluded if the "Colour" variation is NOT Blue
"Green": {
"include_when": { "AND": { "Colour": "Yellow" }, { "Hat": "Beret" } }, // "Green" variation for this layer is included if Colour is Yellow AND Hat is Beret
"exclude_when": { "OR": { "Colour": "Yellow" }, { "Hat": "Cowboy" } } // "Green" variation for this layer is excluded if Colour is Yellow OR Hat is Cowboy
}
}
Proof of concept of the variation conditionals working with the new random sampler is here 10c36338a2c992b1b2ae1f39e7decff03ca771d7
It implements a single replacement algorithm, and allows for conditions on layer i
to depend on any layer j != i
Example of a validation callback for the random sampler:
rs = RandomSampler(weights)
rs.set_validation_callback( # layer["Color"] == 'Blue' and layer["Landscape"] == 'Tunnel' is invalid
lambda pick: True if len(pick) < 2 else not (pick[0] == 2 and pick[1] == 2),
validate_each_layer=True
)
No parsing of the conditions from JSON or UI yet.
Turns out, we don't need to re-invent the wheel! Boolean expressions can be represented by a binary expression tree
To keep it JSON compatible, we can keep using dictionaries to represent the tree in Python and in JSON.
Binary operators (values of op
key):
-
OR
: evaluated tonode['left'] OR node['right']
-
AND
: evaluated tonode['left'] AND node['right']
Unary operators (only left
key parsed):
-
NOT
: evaluated toNOT node['left']
Leaves "operators" (used to parse values):
-
VALUE
: evaluated tobool(node['value'])
(should betrue
orfalse
) -
VARIATION
: evaluated to
(node['layer'] in layers) and (layers[node['layer']] == node['variation'])
Each layer variation that needs a conditional will follow the same basic syntax:
"conditions": { // Variation-level conditions (a binary expression tree per variation)
"VARIATION_NAME1": {
"op": "AND",
"left": {
// `include_when` binary expression tree
},
"right": {
"op": "NOT",
"left": {
// `exclude_when` binary expression tree
}
}
},
"VARIATION_NAME2": {
// ...
}
}
Example for "Black"
and "Green"
:
"conditions": { // Variation-level conditions (a binary expression tree per variation)
"Black": {
"op": "AND",
"left": { // `include_when` binary expression tree
"op": "VARIATION",
"layer": "Colour",
"variation": "Green"
},
"right": { // `NOT exclude_when` binary expression tree
"op": "NOT",
"left": { "op": "VALUE", "value": false }
}
},
"Green": {
"op": "AND",
"left": { // `include_when` binary expression tree
"op": "AND",
"left": {
"op": "VARIATION",
"layer": "Colour",
"variation": "Yellow"
},
"right": {
"op": "VARIATION",
"layer": "Hat",
"variation": "Beret"
}
},
"right": { // `NOT exclude_when` binary expression tree
"op": "NOT",
"left": { // `exclude_when` binary expression tree
"op": "OR",
"left": {
"op": "VARIATION",
"layer": "Colour",
"variation": "Red"
},
"right": {
"op": "VARIATION",
"layer": "Hat",
"variation": "Cowboy"
}
}
}
}
}