What's the best practice to set up params with cyclic dependencies (like bidirectional unit conversion)

If I want to make a unit conversion app with two text inputs and each one will update the other one correspondingly. In such case cyclic dependencies must be created, and may cause infinite recursion.

Luckily, seems the param library only update if the value changes. For example, if I set c = = f - 1; f == c + 1, when updating c no infinite loop occured except for an extra call to update c to the same value.

However, if the inter-dependent values are not one-to-one mapped, updating one value may lead to infinite recursion because the computed value changes over iterations. I wonder if there is a way to break the cyclic dependency, for instance, by not invoking the update function if it was triggered by itself?

I tried to use @param.depends and self.param.watch, seems both are not able to handle such scenario.

sample code below:

import panel as pn
import param
class UnitConverter(param.Parameterized):
    c = param.Number(default=0)
    f = param.Number(default=0)

    def __init__(self):
        super().__init__()
        self.param.watch(self.update_f,  ["c"], onlychanged=True)
        self.param.watch(self.update_c, ["f"], onlychanged=True)
        
    # @param.depends("c", watch=True)
    def update_f(self, event):
        print(f"c updated, c = {self.c}, event={event}")
        self.f = self.c + 1

    # @param.depends("f", watch=True)
    def update_c(self, event):
        print(f"f updated, f = {self.f}, event={event}")
        # if change to self.c = self.f - 0.5, infinite loop occurs
        self.c = self.f - 1


converter = UnitConverter()
pn.panel(converter)

That’s a good question. I think you can use:

with param.discard_event(self):
   self.c = self.f - 1

to prevent the infinite loop, but if external watches c, then it wont trigger.

I think it’s better to have one source of truth, e.g. have f as a param, and c as a Python property but perhaps someone with more param knowledge can answer!

1 Like

Thank you for your suggestion!
This indeed prevents infinite loop but as you have pointed out, any dependency of c won’t be notified.