Hi community, are there plans on supporting more c...
# ask-the-community
y
Hi community, are there plans on supporting more complicated logic for conditions. As noted on the Flyte documentation:
Copy code
Conditions are limited to certain binary and logical operators and can only be performed on primitive values.
d
Hey Yubo! What kind of conditions did you have in mind?
y
I am thinking about non-primitive types, like we define some custom objects and expose a function that conditional can depend on for the evaluation
@Ankit Goyal FYI
a
@Yubo Wang perhaps a specific example would be helpful to share?
d
Can you mock up a python example maybe?
y
in a sec
Copy code
@dataclass_json
@dataclass
class TFTrainerConfig(object):
    dataset_config: DatasetLoaderConfig
    hyperparameters: Hyperparameters
    column_names: List[str]
    model_code: str
    batch_size: int
    num_training_steps: int
    num_eval_steps: int
    num_eval_frequency: int
    model_output_names_map: Optional[dict]

@workflow
def tf2_trainer_wf(trainer_config: TFTrainerConfig):
    res = conditonal("batch_size_greater")
          .if_(trainer_config.batch_size > 10)
          .then()
something like this
and also something like this:
Copy code
@workflow
def tf2_trainer_wf(trainer_config: TFTrainerConfig):
    res = conditonal("batch_size_greater")
          .if_(trainer_config.model_output_names_map not None)
          .then()
d
Yeah, so this would be very difficult to do using conditionals. Conditionals are evaluated using BranchNodes within propeller, which are able to be processed interally given the simple boolean logic. For this, you would basically need to spin up a Flyte task to evaluate. It really doesn't fit the current BranchNode setup. I'm wondering if this can be done with dynamic tasks though:
Copy code
@task
def process_success(...):
    # foo

@dynamic
def tf2_trainer_wf(trainer_config: TFTrainerConfig):
    trainer_config.batch_size > 10:
        process_success()
of course this spins up a separate Pod to process this and compiles / injects a new DAG. So there is some level of overhead.
Or you could have a separate task to return the condition variable:
Copy code
@task
def process(...) bool:
    return trainer_config.model_output_names_map == None

@workflow
def tf2_trainer_wf(trainer_config: TFTrainerConfig):
    condition = process(trainer_config)

    res = conditonal("batch_size_greater")
          .if_(condition)
          .then()
which also has some level of overhead obviously.
y
thanks for the suggestion, but we were trying to avoid dynamic workflows due to the overhead.
d
sure, makes plenty of sense.
y
I am looking into the implementation of BranchNode
a
so is the only thing that doesn't work here is that
trainer_config
is a dataclass? If it was just an int, it would work; right?
y
yes, that is what I am doing for now
inputting as flags like
use_model_bundle_creator
interesting, @Dan Rammer (hamersaw) Does condition support chaining? When I put
Copy code
@workflow
def tf2_trainer_wf(use_model_bundle_creator: bool):
    trainer_res = trainer_task()
    res = conditional("use_model_bundle_creator").if_(use_model_bundle_creator.is_true()).then(model_bundle_creator())
    trainer_res >> res
I got
AttributeError: 'Condition' object has no attribute 'ref'
d
@Eduardo Apolinario (eapolinario) how are
dataclasses
stored as literals? If there was some way to encode the specific field of the dataclass in a
Literal
proto then in propeller when we evaluate in the BranchNode you could look up the field and return the primitive value.
re: chaining - another question for eduardo. I handle mostly backend stuff and can't speak for flytekit syntax with any authority 😅 cc @Niels Bantilan
y
Thanks Dan!
d
No problem. Let me know if you find anything in BranchNode implementation. I just took a quick look, I think with some changes to Literal if we can parse things our correctly from flytekit the backend may be able to retrieve the primitive value. We would have to have a larger discussion on this, but it could be very useful.
e
@Dan Rammer (hamersaw), sorry for the delay, was afk for a bunch of hours. Regarding how we store
dataclasses
as literals. We lean pretty heavily on protobuf Struct.
y
hey @Eduardo Apolinario (eapolinario) thanks for looking at this. so this means that we can actually implement some parsing as long as the input is decorated with @dataclass_json?
e
@Yubo Wang, yeah, I think so. We'd need to change a few things in the implementation of conditionals in propeller, but nothing dramatic AFAIU. Dan to correct me.
y
Another question regarding to conditionals, seems to me that the conditionals do not support empty else clause for example:
Copy code
conditional("use_model_bundle_creator").if_(use_model_bundle_creator.is_true()).then(model_bundle_creator()).else_()
s
Yeah. Empty
else
isn't supported. I think it makes sense to mandatorily input what needs to be done in case
if
isn't traversed.
y
what if I expected nothing to be done for the else clause
s
That isn't possible. You either need to call a task or initialize
fail
n
the
conditional
construct is more like
Copy code
x = 0 if cond else 1
as opposed to
Copy code
if cond:
    x = 0
... # technically don't need an else here
i.e. all conditions must be specified. You could use a
noop
task in the else clause to workaround this
y
@Niels Bantilan Any example that I can refer to? I can’t seem to find noop task on the official doc or github repos
n
oh, just like:
Copy code
@task
def noop(): ...
y
does that require spinning up a container to handle it? I assume it would
Hi @Niels Bantilan I have this question: We have this use case that we would add in some tasks/subflows based on the user inputs by leveraging Flyte’s conditions. As you suggested using a noop task which is at cost of spinning up a container task. I am thinking about if we introduce an
end()
function similar to the use of fail() that allows us to do nothing on a branch.
Do you think the above solution is doable?
s
Makes sense. @Niels Bantilan WDYT?
n
yep, this use case makes sense! @Yubo Wang can you create a [flyte-core] issue below 👇 to support the
end()
use case? If I understand correctly, this would be for cases where you don’t want to do any thing in one of the conditional branches, right?
d
@Yubo Wang is this something you might be willing to contribute? It sounds like you already dove through the propeller code a bit, but would be happy to help!
y
Sounds good, I will be working on this
110 Views