LooPyGen icon indicating copy to clipboard operation
LooPyGen copied to clipboard

Variation conditionals

Open sk33z3r opened this issue 2 years ago • 5 comments

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
}

sk33z3r avatar Jun 23 '22 17:06 sk33z3r

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 layers j < i
  • [ ] Parsing of the conditions layer key into boolean expressions
  • [ ] In-UI conditions "builder"

Montspy avatar Jun 29 '22 05:06 Montspy

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
    }
}

Montspy avatar Jul 07 '22 04:07 Montspy

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.

Montspy avatar Jul 07 '22 06:07 Montspy

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 to node['left'] OR node['right']
  • AND: evaluated to node['left'] AND node['right']

Unary operators (only left key parsed):

  • NOT: evaluated to NOT node['left']

Leaves "operators" (used to parse values):

  • VALUE: evaluated to bool(node['value']) (should be true or false)
  • 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"
                }
            }
        }
    }
}

Montspy avatar Jul 08 '22 00:07 Montspy