%23%20gallery_category%3A%20FDN%20Design%20%26%20Analysis%0A%0Aimport%20marimo%0A%0A__generated_with%20%3D%20%220.23.13%22%0Aapp%20%3D%20marimo.App()%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%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%20FDN%20with%20spread%20modal%20decay%0A%0A%20%20%20%20Demonstrates%20an%20FDN%20*without*%20homogeneous%20decay%2C%20but%20with%20a%20certain%20decay%0A%20%20%20%20spread%2C%20as%20is%20typically%20observed%20in%20shoebox%20rooms%20and%20scattering%20delay%0A%20%20%20%20networks.%20The%20spread%20is%20evaluated%20via%20the%20modal%20decomposition.%0A%0A%20%20%20%20Two%20feedback%20matrices%20with%20identical%20gain-per-sample%20energy%3A%0A%0A%20%20%20%20-%20**Proportional**%3A%20%24A%20%3D%20Q%5C%2C%5CGamma%24%20with%20orthogonal%20%24Q%24%20and%20delay-proportional%0A%20%20%20%20%20%20gains%20%24%5CGamma%20%3D%20%5Cmathrm%7Bdiag%7D(g%5E%7Bm_i%7D)%24%20%E2%86%92%20all%20modes%20decay%20with%20the%20same%20T60.%0A%20%20%20%20-%20**Spread**%3A%20%24A%20%3D%20Q_1%5C%2C%5CGamma%5C%2CQ_2%24%20%E2%86%92%20the%20second%20rotation%20distributes%20the%0A%20%20%20%20%20%20absorption%20unevenly%20over%20the%20modes%2C%20spreading%20their%20T60s.%0A%0A%20%20%20%20Reference%3A%20*Schlecht%2C%20S.%2C%20Habets%2C%20E.%20(2019).%20Modal%20Decomposition%20of%20Feedback%0A%20%20%20%20Delay%20Networks.%20IEEE%20Transactions%20on%20Signal%20Processing%2067(20)%2C%205340-5351.*%0A%20%20%20%20%5Bdoi%3A10.1109%2Ftsp.2019.2937286%5D(https%3A%2F%2Fdx.doi.org%2F10.1109%2Ftsp.2019.2937286)%0A%0A%20%20%20%20Original%20MATLAB%3A%20%60example_spreadFDNpoles.m%60%2C%20Sebastian%20J.%20Schlecht%2C%2023%20April%202018.%0A%20%20%20%20Delays%20are%20scaled%20down%20relative%20to%20the%20MATLAB%20script%20to%20keep%20the%0A%20%20%20%20eigendecomposition%20fast%3B%20the%20qualitative%20behaviour%20is%20unchanged.%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%20numpy%20as%20np%0A%20%20%20%20import%20plotly.graph_objects%20as%20go%0A%20%20%20%20import%20plotly.io%20as%20pio%0A%0A%20%20%20%20import%20pyFDN%0A%0A%20%20%20%20pio.renderers.default%20%3D%20%22sphinx_gallery%22%0A%20%20%20%20return%20go%2C%20np%2C%20pyFDN%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%20Define%20FDN%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(np%2C%20pyFDN)%3A%0A%20%20%20%20np.random.seed(1)%0A%20%20%20%20fs%20%3D%2048000%0A%20%20%20%20types%20%3D%20%5B%22Proportional%22%2C%20%22Spread%22%5D%0A%0A%20%20%20%20rt%20%3D%201.0%20%20%23%20seconds%0A%20%20%20%20ir_len%20%3D%20int(rt%20*%20fs)%0A%20%20%20%20gain_per_sample%20%3D%20pyFDN.rt_to_gain_per_sample(rt%2C%20fs)%0A%0A%20%20%20%20num_delays%20%3D%208%0A%20%20%20%20delays%20%3D%20np.random.randint(100%2C%20301%2C%20num_delays)%0A%20%20%20%20input_gain%20%3D%20np.eye(num_delays%2C%201)%0A%20%20%20%20output_gain%20%3D%20np.eye(1%2C%20num_delays)%0A%20%20%20%20direct%20%3D%20np.zeros((1%2C%201))%0A%0A%20%20%20%20gain_matrix%20%3D%20np.diag(gain_per_sample%20**%20delays.astype(float))%0A%20%20%20%20feedback_matrix%20%3D%20%7B%0A%20%20%20%20%20%20%20%20%22Proportional%22%3A%20pyFDN.random_orthogonal(num_delays)%20%40%20gain_matrix%2C%0A%20%20%20%20%20%20%20%20%22Spread%22%3A%20pyFDN.random_orthogonal(num_delays)%0A%20%20%20%20%20%20%20%20%40%20gain_matrix%0A%20%20%20%20%20%20%20%20%40%20pyFDN.random_orthogonal(num_delays)%2C%0A%20%20%20%20%7D%0A%20%20%20%20print(f%22Delays%3A%20%7Bdelays%7D%20(sum%20%3D%20%7Bdelays.sum()%7D)%22)%0A%20%20%20%20return%20(%0A%20%20%20%20%20%20%20%20delays%2C%0A%20%20%20%20%20%20%20%20direct%2C%0A%20%20%20%20%20%20%20%20feedback_matrix%2C%0A%20%20%20%20%20%20%20%20fs%2C%0A%20%20%20%20%20%20%20%20input_gain%2C%0A%20%20%20%20%20%20%20%20ir_len%2C%0A%20%20%20%20%20%20%20%20output_gain%2C%0A%20%20%20%20%20%20%20%20rt%2C%0A%20%20%20%20%20%20%20%20types%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%20Modal%20decomposition%2C%20impulse%20response%2C%20energy%20decay%20curve%0A%0A%20%20%20%20For%20each%20matrix%20type%2C%20compute%20poles%2Fresidues%20with%20%60dss_to_pr%60%2C%0A%20%20%20%20synthesize%20the%20impulse%20response%20from%20the%20modes%20with%20%60pr_to_impz%60%2C%20and%0A%20%20%20%20compute%20the%20energy%20decay%20curve.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20delays%2C%0A%20%20%20%20direct%2C%0A%20%20%20%20feedback_matrix%2C%0A%20%20%20%20input_gain%2C%0A%20%20%20%20ir_len%2C%0A%20%20%20%20output_gain%2C%0A%20%20%20%20pyFDN%2C%0A%20%20%20%20types%2C%0A)%3A%0A%20%20%20%20poles%20%3D%20%7B%7D%0A%20%20%20%20edcs%20%3D%20%7B%7D%0A%20%20%20%20for%20_type%20in%20types%3A%0A%20%20%20%20%20%20%20%20_res%2C%20_pol%2C%20_direct_term%2C%20_is_pair%2C%20_%20%3D%20pyFDN.dss_to_pr(%0A%20%20%20%20%20%20%20%20%20%20%20%20delays%2C%20feedback_matrix%5B_type%5D%2C%20input_gain%2C%20output_gain%2C%20direct%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20poles%5B_type%5D%20%3D%20_pol%0A%20%20%20%20%20%20%20%20_ir%20%3D%20pyFDN.pr_to_impz(%0A%20%20%20%20%20%20%20%20%20%20%20%20_res%2C%20_pol%2C%20_direct_term%2C%20_is_pair%2C%20ir_len%2C%20mode%3D%22lowMemory%22%0A%20%20%20%20%20%20%20%20)%5B%3A%2C%200%2C%200%5D%0A%20%20%20%20%20%20%20%20edcs%5B_type%5D%20%3D%20pyFDN.sq_to_db(pyFDN.edc(_ir))%0A%20%20%20%20%20%20%20%20print(f%22%7B_type%7D%3A%20%7B_pol.size%7D%20poles%22)%0A%20%20%20%20return%20edcs%2C%20poles%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%20Energy%20decay%20curves%0A%0A%20%20%20%20The%20proportional%20FDN%20decays%20along%20a%20straight%20line%3B%20the%20spread%20FDN%20bends%2C%0A%20%20%20%20because%20slowly%20decaying%20modes%20dominate%20the%20late%20tail.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(edcs%2C%20fs%2C%20go%2C%20ir_len%2C%20np%2C%20types)%3A%0A%20%20%20%20fig_edc%20%3D%20go.Figure()%0A%20%20%20%20t_axis%20%3D%20np.arange(ir_len)%20%2F%20fs%0A%20%20%20%20for%20_type%20in%20types%3A%0A%20%20%20%20%20%20%20%20fig_edc.add_trace(go.Scatter(x%3Dt_axis%2C%20y%3Dedcs%5B_type%5D%2C%20mode%3D%22lines%22%2C%20name%3D_type))%0A%20%20%20%20fig_edc.update_layout(%0A%20%20%20%20%20%20%20%20title%3D%22Energy%20decay%20curve%22%2C%0A%20%20%20%20%20%20%20%20xaxis%3D%7B%22title%22%3A%20%22Time%20(s)%22%7D%2C%0A%20%20%20%20%20%20%20%20yaxis%3D%7B%22title%22%3A%20%22Energy%20Decay%20Curve%20(dB)%22%7D%2C%0A%20%20%20%20%20%20%20%20template%3D%22plotly_white%22%2C%0A%20%20%20%20%20%20%20%20height%3D420%2C%0A%20%20%20%20)%0A%20%20%20%20fig_edc.show()%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%20Pole%20T60s%0A%0A%20%20%20%20Per-mode%20reverberation%20time%20over%20pole%20angle.%20The%20proportional%20matrix%20puts%0A%20%20%20%20all%20poles%20on%20a%20single%20T60%20line%3B%20the%20spread%20matrix%20scatters%20them.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(fs%2C%20go%2C%20np%2C%20poles%2C%20pyFDN%2C%20rt%2C%20types)%3A%0A%20%20%20%20fig_poles%20%3D%20go.Figure()%0A%20%20%20%20for%20_type%20in%20types%3A%0A%20%20%20%20%20%20%20%20_pol%20%3D%20poles%5B_type%5D%0A%20%20%20%20%20%20%20%20fig_poles.add_trace(%0A%20%20%20%20%20%20%20%20%20%20%20%20go.Scatter(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20x%3Dnp.angle(_pol)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20y%3DpyFDN.slope_to_rt(pyFDN.lin_to_db(np.abs(_pol))%2C%20fs)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20mode%3D%22markers%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20marker%3D%7B%22size%22%3A%204%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20name%3D_type%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20fig_poles.update_layout(%0A%20%20%20%20%20%20%20%20title%3D%22Modal%20reverberation%20times%22%2C%0A%20%20%20%20%20%20%20%20xaxis%3D%7B%22title%22%3A%20%22Pole%20angle%20(rad)%22%7D%2C%0A%20%20%20%20%20%20%20%20yaxis%3D%7B%22title%22%3A%20%22Pole%20T60%20(s)%22%7D%2C%0A%20%20%20%20%20%20%20%20template%3D%22plotly_white%22%2C%0A%20%20%20%20%20%20%20%20yaxis_range%3D%5B0%2C%20rt%20*%201.5%5D%2C%0A%20%20%20%20%20%20%20%20height%3D420%2C%0A%20%20%20%20)%0A%20%20%20%20fig_poles.show()%0A%20%20%20%20return%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A
dffc3aac2bc6bcc8e53e89c0dd0f5f31