%22%22%22Example%3A%20Building%20a%20large%20computation%20with%20repeated%20blocks.%22%22%22%0A%0A%23%20ruff%3A%20noqa%3A%20TRY003%0A%0A%23%20%2F%2F%2F%20script%0A%23%20requires-python%20%3D%20%22%3E%3D3.11%22%0A%23%20dependencies%20%3D%20%5B%0A%23%20%20%20%20%20%22marimo%3D%3D0.23.0%22%2C%0A%23%20%20%20%20%20%22numpy%22%2C%0A%23%20%20%20%20%20%22pandas%22%2C%0A%23%20%20%20%20%20%22loman%22%2C%0A%23%20%5D%0A%23%0A%23%20%5Btool.uv.sources%5D%0A%23%20loman%20%3D%20%7B%20path%20%3D%20%22..%2F..%22%2C%20editable%20%3D%20true%20%7D%0A%23%20%2F%2F%2F%0A%0Aimport%20marimo%0A%0A__generated_with%20%3D%20%220.23.0%22%0Aapp%20%3D%20marimo.App(width%3D%22full%22)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%20Large%20Repeated%20Blocks%0A%0A%20%20%20%20This%20notebook%20builds%20a%20portfolio%20computation%20with%20one%20reusable%20block%20per%0A%20%20%20%20instrument.%20It%20demonstrates%20every%20computation%20utility%20in%20%60loman.util%60%3A%0A%0A%20%20%20%20-%20%60add_repeated_blocks%60%20creates%20keyed%20copies%20of%20a%20calculation%20block.%0A%20%20%20%20-%20%60add_fan_out%60%20slices%20shared%20market%20data%20and%20broadcasts%20shared%20settings.%0A%20%20%20%20-%20%60add_fan_in%60%20concatenates%20instrument%20reports%20and%20calculates%20totals.%0A%20%20%20%20-%20%60RepeatedBlocks%60%20composes%20features%20such%20as%20%60FanOut%60%20and%20%60FanIn%60%20into%20a%20reusable%20graph%20builder.%0A%0A%20%20%20%20The%20example%20uses%2024%20instruments%20and%20finishes%20with%20more%20than%20220%20ordinary%0A%20%20%20%20Loman%20nodes.%20The%20utilities%20only%20build%20the%20graph%3A%20values%20are%20still%20evaluated%0A%20%20%20%20lazily%20by%20the%20standard%20Loman%20scheduler.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20from%20concurrent.futures%20import%20ThreadPoolExecutor%0A%0A%20%20%20%20import%20numpy%20as%20np%0A%20%20%20%20import%20pandas%20as%20pd%0A%0A%20%20%20%20from%20loman%20import%20Computation%2C%20States%2C%20util%0A%0A%20%20%20%20def%20select_instrument(frame%2C%20instrument_id)%3A%0A%20%20%20%20%20%20%20%20%22%22%22Select%20one%20instrument%20while%20retaining%20a%20dataframe%20result.%22%22%22%0A%20%20%20%20%20%20%20%20return%20frame.loc%5B%5Binstrument_id%5D%5D.copy()%0A%0A%20%20%20%20def%20concat_instruments(frames)%3A%0A%20%20%20%20%20%20%20%20%22%22%22Concatenate%20keyed%20instrument%20dataframes.%22%22%22%0A%20%20%20%20%20%20%20%20return%20pd.concat(frames%2C%20names%3D%5B%22instrument_id%22%5D)%0A%0A%20%20%20%20def%20aggregate_portfolio(frames)%3A%0A%20%20%20%20%20%20%20%20%22%22%22Aggregate%20selected%20numeric%20columns%20from%20keyed%20reports.%22%22%22%0A%20%20%20%20%20%20%20%20report%20%3D%20pd.concat(frames%2C%20names%3D%5B%22instrument_id%22%5D)%0A%20%20%20%20%20%20%20%20return%20report%5B%5B%22market_value%22%2C%20%22daily_pnl%22%2C%20%22var_95%22%5D%5D.sum()%0A%0A%20%20%20%20return%20(%0A%20%20%20%20%20%20%20%20Computation%2C%0A%20%20%20%20%20%20%20%20States%2C%0A%20%20%20%20%20%20%20%20ThreadPoolExecutor%2C%0A%20%20%20%20%20%20%20%20aggregate_portfolio%2C%0A%20%20%20%20%20%20%20%20concat_instruments%2C%0A%20%20%20%20%20%20%20%20np%2C%0A%20%20%20%20%20%20%20%20pd%2C%0A%20%20%20%20%20%20%20%20select_instrument%2C%0A%20%20%20%20%20%20%20%20util%2C%0A%20%20%20%20)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%201.%20Define%20one%20reusable%20instrument%20block%0A%0A%20%20%20%20The%20block%20has%20two%20inputs%20and%20five%20calculations.%20%60instrument_data%60%20receives%0A%20%20%20%20one%20row%20sliced%20from%20the%20shared%20market%20dataframe.%20%60risk_settings%60%20receives%0A%20%20%20%20the%20same%20settings%20object%20for%20every%20block.%0A%0A%20%20%20%20%60audit%60%20deliberately%20sits%20outside%20the%20path%20to%20%60report%60.%20It%20makes%20the%20graph%0A%20%20%20%20larger%20while%20demonstrating%20that%20unused%20branches%20are%20not%20computed.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(Computation)%3A%0A%20%20%20%20instrument_block%20%3D%20Computation()%0A%20%20%20%20instrument_block.add_node(%22instrument_data%22)%0A%20%20%20%20instrument_block.add_node(%22risk_settings%22)%0A%0A%20%20%20%20def%20validate_instrument(instrument_data)%3A%0A%20%20%20%20%20%20%20%20required%20%3D%20%7B%22quantity%22%2C%20%22price%22%2C%20%22previous_close%22%2C%20%22volatility%22%2C%20%22beta%22%7D%0A%20%20%20%20%20%20%20%20missing%20%3D%20required%20-%20set(instrument_data.columns)%0A%20%20%20%20%20%20%20%20if%20missing%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20raise%20ValueError(f%22Missing%20instrument%20fields%3A%20%7Bsorted(missing)%7D%22)%0A%20%20%20%20%20%20%20%20return%20instrument_data.copy()%0A%0A%20%20%20%20def%20value_instrument(validate_instrument)%3A%0A%20%20%20%20%20%20%20%20result%20%3D%20validate_instrument.copy()%0A%20%20%20%20%20%20%20%20result%5B%22market_value%22%5D%20%3D%20result%5B%22quantity%22%5D%20*%20result%5B%22price%22%5D%0A%20%20%20%20%20%20%20%20result%5B%22daily_pnl%22%5D%20%3D%20result%5B%22quantity%22%5D%20*%20(result%5B%22price%22%5D%20-%20result%5B%22previous_close%22%5D)%0A%20%20%20%20%20%20%20%20return%20result%0A%0A%20%20%20%20def%20calculate_risk(value_instrument%2C%20risk_settings)%3A%0A%20%20%20%20%20%20%20%20result%20%3D%20value_instrument.copy()%0A%20%20%20%20%20%20%20%20horizon%20%3D%20risk_settings%5B%22horizon_days%22%5D%20**%200.5%0A%20%20%20%20%20%20%20%20result%5B%22var_95%22%5D%20%3D%20(%0A%20%20%20%20%20%20%20%20%20%20%20%20result%5B%22market_value%22%5D.abs()%20*%20result%5B%22volatility%22%5D%20*%20risk_settings%5B%22confidence_multiplier%22%5D%20*%20horizon%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20result%5B%22beta_exposure%22%5D%20%3D%20result%5B%22market_value%22%5D%20*%20result%5B%22beta%22%5D%0A%20%20%20%20%20%20%20%20return%20result%0A%0A%20%20%20%20def%20format_report(calculate_risk)%3A%0A%20%20%20%20%20%20%20%20columns%20%3D%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22sector%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22quantity%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22price%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22market_value%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22daily_pnl%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22var_95%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22beta_exposure%22%2C%0A%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%20%20return%20calculate_risk%5Bcolumns%5D%0A%0A%20%20%20%20def%20build_audit(calculate_risk)%3A%0A%20%20%20%20%20%20%20%20return%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22rows%22%3A%20len(calculate_risk)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22columns%22%3A%20tuple(calculate_risk.columns)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22checked%22%3A%20True%2C%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20instrument_block.add_node(%22validated%22%2C%20validate_instrument%2C%20kwds%3D%7B%22instrument_data%22%3A%20%22instrument_data%22%7D)%0A%20%20%20%20instrument_block.add_node(%22valued%22%2C%20value_instrument%2C%20kwds%3D%7B%22validate_instrument%22%3A%20%22validated%22%7D)%0A%20%20%20%20instrument_block.add_node(%0A%20%20%20%20%20%20%20%20%22risk%22%2C%0A%20%20%20%20%20%20%20%20calculate_risk%2C%0A%20%20%20%20%20%20%20%20kwds%3D%7B%22value_instrument%22%3A%20%22valued%22%2C%20%22risk_settings%22%3A%20%22risk_settings%22%7D%2C%0A%20%20%20%20)%0A%20%20%20%20instrument_block.add_node(%22report%22%2C%20format_report%2C%20kwds%3D%7B%22calculate_risk%22%3A%20%22risk%22%7D)%0A%20%20%20%20instrument_block.add_node(%22audit%22%2C%20build_audit%2C%20kwds%3D%7B%22calculate_risk%22%3A%20%22risk%22%7D)%0A%20%20%20%20instrument_block.draw(collapse_all%3DFalse%2C%20graph_attr%3D%7B%22rankdir%22%3A%20%22LR%22%7D)%0A%20%20%20%20return%20(instrument_block%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%202.%20Create%20deterministic%20portfolio%20data%0A%0A%20%20%20%20The%20portfolio%20has%2024%20instruments%20spread%20across%20four%20sectors.%20A%20thread%20pool%0A%20%20%20%20lets%20independent%20instrument%20blocks%20run%20concurrently%3B%20the%20graph%20would%20work%0A%20%20%20%20identically%20with%20Loman's%20default%20executor.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(Computation%2C%20ThreadPoolExecutor%2C%20np%2C%20pd)%3A%0A%20%20%20%20instrument_ids%20%3D%20tuple(f%22INST-%7Bnumber%3A03d%7D%22%20for%20number%20in%20range(1%2C%2025))%0A%20%20%20%20sectors%20%3D%20(%22Technology%22%2C%20%22Financials%22%2C%20%22Healthcare%22%2C%20%22Industrials%22)%0A%20%20%20%20positions%20%3D%20np.arange(1%2C%20len(instrument_ids)%20%2B%201)%0A%0A%20%20%20%20market_data%20%3D%20pd.DataFrame(%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22sector%22%3A%20%5Bsectors%5Bindex%20%25%20len(sectors)%5D%20for%20index%20in%20range(len(instrument_ids))%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22quantity%22%3A%20np.where(positions%20%25%205%20%3D%3D%200%2C%20-positions%20*%207%2C%20positions%20*%2010)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22previous_close%22%3A%2080.0%20%2B%20positions%20*%202.25%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22price%22%3A%2080.0%20%2B%20positions%20*%202.25%20%2B%20np.sin(positions)%20*%201.5%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22volatility%22%3A%200.12%20%2B%20(positions%20%25%208)%20*%200.015%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22beta%22%3A%200.75%20%2B%20(positions%20%25%207)%20*%200.08%2C%0A%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20index%3Dpd.Index(instrument_ids%2C%20name%3D%22instrument_id%22)%2C%0A%20%20%20%20)%0A%20%20%20%20risk_settings%20%3D%20%7B%22confidence_multiplier%22%3A%201.65%2C%20%22horizon_days%22%3A%2010%7D%0A%0A%20%20%20%20executor%20%3D%20ThreadPoolExecutor(max_workers%3D8)%0A%20%20%20%20comp%20%3D%20Computation(default_executor%3Dexecutor)%0A%20%20%20%20comp.add_node(%22market_data%22%2C%20value%3Dmarket_data)%0A%20%20%20%20comp.add_node(%22risk_settings%22%2C%20value%3Drisk_settings)%0A%20%20%20%20market_data%0A%20%20%20%20return%20comp%2C%20instrument_ids%2C%20market_data%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%203.%20Compose%20repeated%20blocks%2C%20fan-out%2C%20and%20fan-in%0A%0A%20%20%20%20The%20three%20small%20helpers%20can%20be%20combined%20explicitly%20when%20a%20block%20has%20several%0A%20%20%20%20inputs%20or%20the%20graph%20needs%20several%20aggregate%20outputs.%0A%0A%20%20%20%20-%20The%20first%20fan-out%20calls%20%60select_instrument(market_data%2C%20instrument_id)%60.%0A%20%20%20%20-%20The%20second%20broadcasts%20%60risk_settings%60%20unchanged.%0A%20%20%20%20-%20Two%20fan-ins%20consume%20the%20same%20per-instrument%20reports%3A%20one%20concatenates%20all%0A%20%20%20%20%20%20rows%2C%20while%20the%20other%20returns%20portfolio%20totals.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20aggregate_portfolio%2C%0A%20%20%20%20comp%2C%0A%20%20%20%20concat_instruments%2C%0A%20%20%20%20instrument_block%2C%0A%20%20%20%20instrument_ids%2C%0A%20%20%20%20select_instrument%2C%0A%20%20%20%20util%2C%0A)%3A%0A%20%20%20%20blocks%20%3D%20util.add_repeated_blocks(%0A%20%20%20%20%20%20%20%20comp%2C%0A%20%20%20%20%20%20%20%20instrument_block%2C%0A%20%20%20%20%20%20%20%20instrument_ids%2C%0A%20%20%20%20%20%20%20%20base_path%3D%22instruments%22%2C%0A%20%20%20%20)%0A%0A%20%20%20%20instrument_inputs%20%3D%20%7Bkey%3A%20path%20%2F%20%22instrument_data%22%20for%20key%2C%20path%20in%20blocks.items()%7D%0A%20%20%20%20util.add_fan_out(comp%2C%20%22market_data%22%2C%20instrument_inputs%2C%20transform%3Dselect_instrument)%0A%0A%20%20%20%20settings_inputs%20%3D%20%7Bkey%3A%20path%20%2F%20%22risk_settings%22%20for%20key%2C%20path%20in%20blocks.items()%7D%0A%20%20%20%20util.add_fan_out(comp%2C%20%22risk_settings%22%2C%20settings_inputs)%0A%0A%20%20%20%20report_sources%20%3D%20%7Bkey%3A%20path%20%2F%20%22report%22%20for%20key%2C%20path%20in%20blocks.items()%7D%0A%20%20%20%20util.add_fan_in(comp%2C%20%22portfolio_report%22%2C%20report_sources%2C%20combine%3Dconcat_instruments)%0A%20%20%20%20util.add_fan_in(comp%2C%20%22portfolio_totals%22%2C%20report_sources%2C%20combine%3Daggregate_portfolio)%0A%20%20%20%20comp%0A%20%20%20%20return%20(blocks%2C)%0A%0A%0A%40app.cell%0Adef%20_(comp%2C%20pd)%3A%0A%20%20%20%20graph_size%20%3D%20pd.Series(%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22nodes%22%3A%20len(comp.nodes())%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22dependency_edges%22%3A%20comp.dag.number_of_edges()%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22instrument_blocks%22%3A%2024%2C%0A%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20name%3D%22main%20portfolio%20graph%22%2C%0A%20%20%20%20)%0A%20%20%20%20graph_size%0A%20%20%20%20return%20(graph_size%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20The%20detailed%20graph%20has%20more%20than%20170%20nodes.%20Loman's%20block-aware%20view%20keeps%0A%20%20%20%20it%20readable%20by%20collapsing%20each%20repeated%20namespace%20to%20one%20block%20node.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(comp)%3A%0A%20%20%20%20comp.draw(collapse_all%3DTrue%2C%20graph_attr%3D%7B%22rankdir%22%3A%20%22LR%22%2C%20%22size%22%3A%20%2214%22%7D)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(comp)%3A%0A%20%20%20%20comp.draw(%0A%20%20%20%20%20%20%20%20%22instruments%22%2C%0A%20%20%20%20%20%20%20%20node_transformations%3D%7B%22instruments%2FINST-001%22%3A%20%22expand%22%7D%2C%0A%20%20%20%20%20%20%20%20graph_attr%3D%7B%22rankdir%22%3A%20%22LR%22%2C%20%22size%22%3A%20%2220%22%7D%2C%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%204.%20Compute%20only%20the%20requested%20outputs%0A%0A%20%20%20%20Computing%20the%20two%20portfolio%20outputs%20evaluates%20their%20shared%20ancestors%20once.%0A%20%20%20%20The%2024%20%60audit%60%20nodes%20become%20computable%20when%20their%20shared%20%60risk%60%20predecessors%0A%20%20%20%20finish%2C%20but%20they%20are%20not%20executed%20because%20neither%20output%20depends%20on%20them.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(comp)%3A%0A%20%20%20%20comp.compute(%5B%22portfolio_report%22%2C%20%22portfolio_totals%22%5D)%0A%20%20%20%20portfolio_report%20%3D%20comp.v.portfolio_report%0A%20%20%20%20portfolio_totals%20%3D%20comp.v.portfolio_totals%0A%20%20%20%20portfolio_totals.to_frame(%22value%22)%0A%20%20%20%20return%20portfolio_report%2C%20portfolio_totals%0A%0A%0A%40app.cell%0Adef%20_(portfolio_report)%3A%0A%20%20%20%20portfolio_report%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(States%2C%20blocks%2C%20comp%2C%20pd)%3A%0A%20%20%20%20audit_states%20%3D%20pd.Series(%0A%20%20%20%20%20%20%20%20%7Bstr(key)%3A%20comp.state(path%20%2F%20%22audit%22).name%20for%20key%2C%20path%20in%20blocks.items()%7D%2C%0A%20%20%20%20%20%20%20%20name%3D%22audit%20state%22%2C%0A%20%20%20%20)%0A%20%20%20%20audit_summary%20%3D%20audit_states.value_counts().rename_axis(%22state%22).to_frame(%22nodes%22)%0A%20%20%20%20assert%20set(audit_states)%20%3D%3D%20%7BStates.COMPUTABLE.name%7D%0A%20%20%20%20audit_summary%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%205.%20Add%20a%20second%20repeated-block%20definition%0A%0A%20%20%20%20The%20dataclass%20API%20packages%20block%2C%20key%2C%20fan-out%2C%20and%20fan-in%20configuration%20in%0A%20%20%20%20one%20reusable%20definition.%20Here%20it%20creates%20another%2024%20blocks%20for%20a%20downside%0A%20%20%20%20stress%20scenario%20and%20combines%20their%20outputs%20into%20%60stress_report%60.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(Computation)%3A%0A%20%20%20%20stress_block%20%3D%20Computation()%0A%20%20%20%20stress_block.add_node(%22data%22)%0A%0A%20%20%20%20def%20apply_stress(data)%3A%0A%20%20%20%20%20%20%20%20result%20%3D%20data.copy()%0A%20%20%20%20%20%20%20%20result%5B%22stressed_price%22%5D%20%3D%20result%5B%22price%22%5D%20*%20(1.0%20-%20result%5B%22shock%22%5D)%0A%20%20%20%20%20%20%20%20result%5B%22stress_pnl%22%5D%20%3D%20result%5B%22quantity%22%5D%20*%20(result%5B%22stressed_price%22%5D%20-%20result%5B%22price%22%5D)%0A%20%20%20%20%20%20%20%20return%20result%5B%5B%22sector%22%2C%20%22price%22%2C%20%22stressed_price%22%2C%20%22stress_pnl%22%5D%5D%0A%0A%20%20%20%20stress_block.add_node(%22scenario%22%2C%20apply_stress)%0A%20%20%20%20return%20(stress_block%2C)%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20comp%2C%0A%20%20%20%20concat_instruments%2C%0A%20%20%20%20instrument_ids%2C%0A%20%20%20%20market_data%2C%0A%20%20%20%20select_instrument%2C%0A%20%20%20%20stress_block%2C%0A%20%20%20%20util%2C%0A)%3A%0A%20%20%20%20sector_shocks%20%3D%20%7B%0A%20%20%20%20%20%20%20%20%22Technology%22%3A%200.12%2C%0A%20%20%20%20%20%20%20%20%22Financials%22%3A%200.09%2C%0A%20%20%20%20%20%20%20%20%22Healthcare%22%3A%200.07%2C%0A%20%20%20%20%20%20%20%20%22Industrials%22%3A%200.10%2C%0A%20%20%20%20%7D%0A%20%20%20%20stress_market_data%20%3D%20market_data.assign(shock%3Dmarket_data%5B%22sector%22%5D.map(sector_shocks))%0A%20%20%20%20comp.add_node(%22stress_market_data%22%2C%20value%3Dstress_market_data)%0A%0A%20%20%20%20stress_definition%20%3D%20util.RepeatedBlocks(%0A%20%20%20%20%20%20%20%20block%3Dstress_block%2C%0A%20%20%20%20%20%20%20%20keys%3Dinstrument_ids%2C%0A%20%20%20%20%20%20%20%20base_path%3D%22stress%2Finstruments%22%2C%0A%20%20%20%20%20%20%20%20features%3D%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20util.FanOut(%22stress_market_data%22%2C%20%22data%22%2C%20transform%3Dselect_instrument)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20util.FanIn(%22scenario%22%2C%20%22stress_report%22%2C%20combine%3Dconcat_instruments)%2C%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20)%0A%20%20%20%20built_stress%20%3D%20stress_definition.add_to(comp)%0A%20%20%20%20built_stress%0A%20%20%20%20return%20(built_stress%2C)%0A%0A%0A%40app.cell%0Adef%20_(comp%2C%20graph_size%2C%20pd)%3A%0A%20%20%20%20expanded_graph_size%20%3D%20pd.DataFrame(%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22before%20stress%20blocks%22%3A%20graph_size%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22after%20stress%20blocks%22%3A%20pd.Series(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22nodes%22%3A%20len(comp.nodes())%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22dependency_edges%22%3A%20comp.dag.number_of_edges()%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22instrument_blocks%22%3A%2048%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20)%0A%20%20%20%20expanded_graph_size%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Even%20though%20all%20stress%20blocks%20exist%2C%20we%20can%20request%20one%20scenario%0A%20%20%20%20node.%20Only%20that%20block's%20required%20calculations%20become%20up%20to%20date.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(States%2C%20built_stress%2C%20comp%2C%20pd)%3A%0A%20%20%20%20selected_stress_node%20%3D%20built_stress.blocks%5B%22INST-007%22%5D%20%2F%20%22scenario%22%0A%20%20%20%20comp.compute(selected_stress_node)%0A%20%20%20%20stress_block_states%20%3D%20pd.Series(%0A%20%20%20%20%20%20%20%20%7Bkey%3A%20comp.state(path%20%2F%20%22scenario%22).name%20for%20key%2C%20path%20in%20built_stress.blocks.items()%7D%2C%0A%20%20%20%20%20%20%20%20name%3D%22scenario%20state%22%2C%0A%20%20%20%20)%0A%20%20%20%20assert%20stress_block_states%5B%22INST-007%22%5D%20%3D%3D%20States.UPTODATE.name%0A%20%20%20%20stress_block_states.value_counts().rename_axis(%22state%22).to_frame(%22nodes%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(comp)%3A%0A%20%20%20%20%23%20single%20computed%20row%0A%20%20%20%20comp.v%5B%22stress%2Finstruments%2FINST-007%2Fscenario%22%5D%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(comp)%3A%0A%20%20%20%20%23%20concatenated%0A%20%20%20%20comp.compute(%22stress_report%22)%0A%20%20%20%20stress_report%20%3D%20comp.v.stress_report%0A%20%20%20%20stress_report%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(comp)%3A%0A%20%20%20%20comp.draw(%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%206.%20Selective%20recalculation%20after%20an%20update%0A%0A%20%20%20%20A%20shallow%20copy%20is%20useful%20for%20experimenting%20without%20changing%20the%20original%0A%20%20%20%20computed%20graph.%20Updating%20the%20shared%20market%20dataframe%20invalidates%20the%20main%0A%20%20%20%20portfolio%20descendants%2C%20but%20asking%20for%20one%20instrument%20report%20recalculates%0A%20%20%20%20only%20that%20required%20branch.%20The%20aggregate%20stays%20stale%20until%20requested.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(States%2C%20blocks%2C%20comp%2C%20market_data%2C%20pd)%3A%0A%20%20%20%20updated_comp%20%3D%20comp.copy()%0A%20%20%20%20updated_market_data%20%3D%20market_data.copy()%0A%20%20%20%20updated_market_data.loc%5B%22INST-012%22%2C%20%22price%22%5D%20%2B%3D%208.0%0A%20%20%20%20updated_comp.insert(%22market_data%22%2C%20updated_market_data)%0A%0A%20%20%20%20changed_report_node%20%3D%20blocks%5B%22INST-012%22%5D%20%2F%20%22report%22%0A%20%20%20%20updated_comp.compute(changed_report_node)%0A%0A%20%20%20%20recalculation_states%20%3D%20pd.Series(%0A%20%20%20%20%20%20%20%20%7Bkey%3A%20updated_comp.state(path%20%2F%20%22report%22).name%20for%20key%2C%20path%20in%20blocks.items()%7D%2C%0A%20%20%20%20%20%20%20%20name%3D%22report%20state%22%2C%0A%20%20%20%20)%0A%20%20%20%20assert%20updated_comp.state(changed_report_node)%20%3D%3D%20States.UPTODATE%0A%20%20%20%20assert%20updated_comp.state(%22portfolio_report%22)%20%3D%3D%20States.STALE%0A%20%20%20%20recalculation_states.value_counts().rename_axis(%22state%22).to_frame(%22nodes%22)%0A%20%20%20%20return%20changed_report_node%2C%20updated_comp%0A%0A%0A%40app.cell%0Adef%20_(changed_report_node%2C%20updated_comp)%3A%0A%20%20%20%20updated_comp.value(changed_report_node)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(portfolio_totals%2C%20updated_comp)%3A%0A%20%20%20%20updated_comp.compute(%22portfolio_totals%22)%0A%20%20%20%20updated_totals%20%3D%20updated_comp.v.portfolio_totals%0A%20%20%20%20totals_comparison%20%3D%20(%0A%20%20%20%20%20%20%20%20portfolio_totals.rename(%22before%22)%0A%20%20%20%20%20%20%20%20.to_frame()%0A%20%20%20%20%20%20%20%20.join(updated_totals.rename(%22after%22))%0A%20%20%20%20%20%20%20%20.assign(change%3Dlambda%20frame%3A%20frame%5B%22after%22%5D%20-%20frame%5B%22before%22%5D)%0A%20%20%20%20)%0A%20%20%20%20totals_comparison%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Summary%0A%0A%20%20%20%20The%20utilities%20create%20normal%20computation%20nodes%20rather%20than%20introducing%20a%0A%20%20%20%20separate%20execution%20model.%20Consequently%2C%20large%20repeated%20graphs%20retain%0A%20%20%20%20Loman's%20core%20properties%3A%0A%0A%20%20%20%20-%20keyed%20blocks%20remain%20inspectable%20and%20visualizable%3B%0A%20%20%20%20-%20fan-out%20transforms%20and%20fan-in%20combiners%20execute%20lazily%3B%0A%20%20%20%20-%20shared%20ancestors%20are%20calculated%20once%3B%0A%20%20%20%20-%20unused%20branches%20stay%20uninitialized%3B%0A%20%20%20%20-%20target%20computation%20limits%20work%20to%20required%20ancestors%3B%0A%20%20%20%20-%20independent%20branches%20use%20the%20configured%20executor.%0A%0A%20%20%20%20For%20JSON%20persistence%2C%20rebuild%20utility-generated%20graphs%20from%20their%20Python%0A%20%20%20%20definition%20before%20recalculation%3B%20the%20current%20serializer%20is%20best%20used%20for%0A%20%20%20%20snapshots%20of%20their%20computed%20values.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20marimo%20as%20mo%0A%0A%20%20%20%20return%20(mo%2C)%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A
b754ff9ac37170454792a678492d5948