Skip to content

Cycles and Scopes

Estimated Time for Completion: 30 minutes; Difficulty Level: Intermediate

In this section, we will explore the intricate relationship between Scopes and Cycles, two important concepts that help manage data nodes and scenarios effectively in Taipy.

Download the code

Cycles

Using Cycles allow you to:

  • Share variables between scenarios in the same time frame

For example, if I have three sales prediction scenarios for the month of June, I do not have to duplicate the data for each scenario. I can share the June sales data between the three scenarios.

  • Better organize the data nodes in your application

Data Node Selector

Here we have a single month_data node for all scenarios of October 2022 and it is part of the October 2022 cycle. I do not need to create a new data node for each scenario and clutter my application.

Example: Filtering by Month

def filter_by_month(df, month):
    df['Date'] = pd.to_datetime(df['Date'])
    df = df[df['Date'].dt.month == month]
    return df

Configuration

Configuration

Configuration

  • Construct the configuration

  • Add the frequency property for the scenario and put "MONTHLY:FREQUENCY" (DAYLY, WEEKLY, MONTHLY, YEARLY)

  • Load the new configuration in the code

A parameter is added in the scenario configuration for the frequency.

from taipy.common.config import Frequency

historical_data_cfg = Config.configure_csv_data_node(id="historical_data",
                                                     default_path="time_series.csv")

month_cfg =  Config.configure_data_node(id="month")
month_values_cfg =  Config.configure_data_node(id="month_values")

task_filter_cfg = Config.configure_task(id="filter_by_month",
                                        function=filter_by_month,
                                        input=[historical_data_cfg, month_cfg],
                                        output=month_values_cfg)


scenario_cfg = Config.configure_scenario(id="my_scenario",
                                         task_configs=[task_filter_cfg],
                                         frequency=Frequency.MONTHLY)

Since we have specified frequency=Frequency.MONTHLY, the corresponding scenario when created, is automatically attached to the correct period (month).

The Cycle which a Scenario belongs to is based on the creation_date of the scenario. It can be "attached" to a specific cycle by manually setting its creation_date, as we are doing in the following example.

tp.Orchestrator().run()

scenario_1 = tp.create_scenario(scenario_cfg,
                                creation_date=dt.datetime(2022,10,7),
                                name="Scenario 2022/10/7")
scenario_2 = tp.create_scenario(scenario_cfg,
                                creation_date=dt.datetime(2022,10,5),
                                name="Scenario 2022/10/5")

Scenario 1 and Scenario 2 are two separate scenario entities created using the same scenario configuration. They are part of the same Cycle but have different data nodes. By default, each scenario instance has its own data node instances, and they are not shared with any other scenario.

Interplay between Scopes and Cycles

Cycles are generated according to the creation_date of scenarios. The scope, on the other hand, determines how data nodes are shared within these cycles and scenarios.

Scopes

Sharing data nodes between entities allows you to organize and manage your data better. It avoids data duplications and allows Taipy to better manage execution (see skippable tasks). The developer may decide:

  • Scope.SCENARIO (default): Having one data node for each scenario.
  • Scope.CYCLE: Extend the scope by sharing data nodes across all scenarios of a given cycle.
  • Scope.GLOBAL: Expand the scope globally, applying it across all scenarios in all cycles.

Modifying the scope of a Data Node is straightforward. Let's change the configuration of our data nodes:

  • historical_data: is a Global data node. It will be shared by every cycle and scenario.

  • month: is a Cycle data node. All scenarios of the same month will share this data.

  • month_values: same for month_values.

Configuration with Scope

Configuration

  • Change the Scope of historical_data to be global

  • Change the Scope of month_data and month to be Cycle

The configuration is the same as the last step except for the data node configurations. New parameter are added for scopes.

from taipy.common.config import Frequency, Scope

historical_data_cfg = Config.configure_csv_data_node(id="historical_data",
                                                     default_path="time_series.csv",
                                                     scope=Scope.GLOBAL)

month_cfg =  Config.configure_data_node(id="month", scope=Scope.CYCLE)
month_values_cfg =  Config.configure_data_node(id="month_values", scope=Scope.CYCLE)

Defining the month of scenario 1 will also determine the month of scenario 2 since they share the same Data Node.

scenario_1.month.write(10)


print("Month Data Node of Scenario 1:", scenario_1.month.read())
print("Month Data Node of Scenario 2:", scenario_2.month.read())

scenario_1.submit()
scenario_2.submit()

Results:

Month Data Node of Scenario 1: 10
Month Data Node of Scenario 2: 10

In this unusual example where both scenarios are in the same cycle and all their data nodes are at least with a Cycle Scope, executing one is the same as executing the other as they share all their data nodes.

Going further into Cycles

Primary scenarios

In each Cycle, there is a primary scenario. A primary scenario is interesting because it represents the important scenario of the Cycle, the reference. By default, the first scenario created for a cycle is primary.

Python code associated to primary scenarios

tp.set_primary(<Scenario>) allows changing the primary scenario in a Cycle.

<Scenario>.is_primary identifies as a Boolean value whether the scenario is primary or not.

before_set_1 = scenario_1.is_primary
before_set_2 = scenario_2.is_primary

tp.set_primary(scenario_2)

print('Scenario 1: Primary?', before_set_1, scenario_1.is_primary)
print('Scenario 2: Primary?', before_set_2, scenario_2.is_primary)

Results:

Scenario 1: Primary? True False
Scenario 2: Primary? False True

Useful functions on cycles

  • tp.get_primary_scenarios(): returns a list of all primary scenarios.

  • tp.get_scenarios(cycle=<Cycle>): returns all the scenarios in the Cycle.

  • tp.get_cycles(): returns the list of Cycles.

  • tp.get_primary(<Cycle>): returns the primary scenario of the Cycle.

Scenario management visual elements

You can use Scenario management visual elements to control Cycles. Cycles can be seen in either the scenario_selector or data_node_selector. Additionally, it's possible to designate a scenario as primary directly through the scenario visual element.

data_node = None
scenario = None

tp.Gui("""<|{scenario}|scenario_selector|>
        <|{scenario}|scenario|>
        <|{scenario}|scenario_dag|>
        <|{data_node}|data_node_selector|>""").run()
import taipy.gui.builder as tgb

data_node = None
scenario = None

with tgb.Page() as page:
    tgb.scenario_selector("{scenario}")
    tgb.scenario("{scenario}")
    tgb.scenario_dag("{scenario}")
    tgb.data_node_selector("{data_node}")
    tgb.data_node("{data_node}")

tp.Gui(page).run()

Visual Elements

Conclusion

By understanding the dynamics between scopes and cycles, developers can effectively manage data nodes and scenarios to suit specific business needs and scenarios. Experiment with different configurations to gain deeper insights into their functionalities and applications.

Entire code

import datetime as dt

import pandas as pd

import taipy as tp
from taipy.common.config import Config, Frequency, Scope


def filter_by_month(df, month):
    df['Date'] = pd.to_datetime(df['Date'])
    df = df[df['Date'].dt.month == month]
    return df

if __name__ == '__main__':
    historical_data_cfg = Config.configure_csv_data_node(id="historical_data",
                                                        default_path="time_series.csv",
                                                        scope=Scope.GLOBAL)
    month_cfg =  Config.configure_data_node(id="month",
                                            scope=Scope.CYCLE)
    month_values_cfg =  Config.configure_data_node(id="month_data",
                                                scope=Scope.CYCLE)


    task_filter_cfg = Config.configure_task(id="filter_by_month",
                                            function=filter_by_month,
                                            input=[historical_data_cfg, month_cfg],
                                            output=month_values_cfg)


    scenario_cfg = Config.configure_scenario(id="my_scenario",
                                            task_configs=[task_filter_cfg],
                                            frequency=Frequency.MONTHLY)

    tp.Orchestrator().run()

    scenario_1 = tp.create_scenario(scenario_cfg,
                                    creation_date=dt.datetime(2022,10,7),
                                    name="Scenario 2022/10/7")
    scenario_2 = tp.create_scenario(scenario_cfg,
                                    creation_date=dt.datetime(2022,10,5),
                                    name="Scenario 2022/10/5")

    scenario_1.month.write(10)

    print("Month Data Node of Scenario 1:", scenario_1.month.read())
    print("Month Data Node of Scenario 2:", scenario_2.month.read())

    scenario_1.submit()

    before_set_1 = scenario_1.is_primary
    before_set_2 = scenario_2.is_primary

    tp.set_primary(scenario_2)

    print('Scenario 1: Primary?', before_set_1, scenario_1.is_primary)
    print('Scenario 2: Primary?', before_set_2, scenario_2.is_primary)

    scenario = None
    data_node = None

    tp.Gui("""<|{scenario}|scenario_selector|>
              <|{scenario}|scenario|>
              <|{scenario}|scenario_dag|>
              <|{data_node}|data_node_selector|>""").run()