-
Notifications
You must be signed in to change notification settings - Fork 0
Add rev11 real-input AZI_CHUNK sweep benchmark, scoped rev12, and rev11-vs-rev12 validator #9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| function results = benchmark_subchunk_agg_check_maxazi_rev11_chunk_sweep_real( ... | ||
| app, ... | ||
| cell_aas_dist_data, ... | ||
| array_bs_azi_data, ... | ||
| radar_beamwidth, ... | ||
| min_azimuth, ... | ||
| max_azimuth, ... | ||
| base_protection_pts, ... | ||
| point_idx, ... | ||
| on_list_bs, ... | ||
| cell_sim_chunk_idx, ... | ||
| rand_seed1, ... | ||
| agg_check_reliability, ... | ||
| on_full_Pr_dBm, ... | ||
| clutter_loss, ... | ||
| custom_antenna_pattern, ... | ||
| sub_point_idx) | ||
| %BENCHMARK_SUBCHUNK_AGG_CHECK_MAXAZI_REV11_CHUNK_SWEEP_REAL | ||
| % Benchmark rev11 with real inputs over a sweep of AZI_CHUNK values. | ||
|
|
||
| if exist('subchunk_agg_check_maxazi_rev11','file')~=2 | ||
| error('benchmark_subchunk_agg_check_maxazi_rev11_chunk_sweep_real:MissingRev11', ... | ||
| 'subchunk_agg_check_maxazi_rev11.m was not found on MATLAB path.'); | ||
| end | ||
|
|
||
| % Required set plus optional boundary values when practical. | ||
| chunk_sizes = [32 64 128 256 512 1024]; | ||
| runtimes = NaN(size(chunk_sizes)); | ||
|
|
||
| fprintf('\n=== REV11 CHUNK SWEEP (REAL INPUTS) ===\n'); | ||
|
|
||
| for idx = 1:numel(chunk_sizes) | ||
| azi_chunk = chunk_sizes(idx); | ||
|
|
||
| f = @() subchunk_agg_check_maxazi_rev11(app,cell_aas_dist_data,array_bs_azi_data, ... | ||
| radar_beamwidth,min_azimuth,max_azimuth,base_protection_pts,point_idx,on_list_bs, ... | ||
| cell_sim_chunk_idx,rand_seed1,agg_check_reliability,on_full_Pr_dBm,clutter_loss, ... | ||
| custom_antenna_pattern,sub_point_idx,azi_chunk); | ||
|
|
||
| try | ||
| if exist('timeit','file')==2 | ||
| runtimes(idx) = timeit(f); | ||
| else | ||
| t0 = tic; | ||
| f(); | ||
| runtimes(idx) = toc(t0); | ||
| end | ||
| catch chunkErr | ||
| runtimes(idx) = NaN; | ||
| warning('benchmark_subchunk_agg_check_maxazi_rev11_chunk_sweep_real:ChunkFailed', ... | ||
| 'Chunk size %d failed (%s). Continuing with remaining chunk sizes.', ... | ||
| azi_chunk, chunkErr.message); | ||
| end | ||
| end | ||
|
|
||
| valid_mask = isfinite(runtimes); | ||
| if ~any(valid_mask) | ||
| error('benchmark_subchunk_agg_check_maxazi_rev11_chunk_sweep_real:NoValidRuns', ... | ||
| 'No successful runtime measurements were produced.'); | ||
| end | ||
|
|
||
| valid_chunks = chunk_sizes(valid_mask); | ||
| valid_runtimes = runtimes(valid_mask); | ||
| [best_runtime,best_idx] = min(valid_runtimes); | ||
| best_chunk = valid_chunks(best_idx); | ||
|
|
||
| fprintf('Chunk Runtime (s) Relative to Best\n'); | ||
| for idx = 1:numel(chunk_sizes) | ||
| if isfinite(runtimes(idx)) | ||
| rel = runtimes(idx) ./ best_runtime; | ||
| fprintf('%5d %10.6f %8.3fx\n',chunk_sizes(idx),runtimes(idx),rel); | ||
| else | ||
| fprintf('%5d %10s %8s\n',chunk_sizes(idx),'FAILED','-'); | ||
| end | ||
| end | ||
|
|
||
| fprintf('Best chunk: %d\n',best_chunk); | ||
| fprintf('Best runtime: %.6f s\n',best_runtime); | ||
|
|
||
| results = struct(); | ||
| results.chunk_sizes = chunk_sizes; | ||
| results.runtimes = runtimes; | ||
| results.best_chunk = best_chunk; | ||
| results.best_runtime = best_runtime; | ||
|
|
||
| idx_128 = find(chunk_sizes==128,1,'first'); | ||
| if ~isempty(idx_128) && isfinite(runtimes(idx_128)) | ||
| results.speedup_vs_128 = runtimes(idx_128) ./ best_runtime; | ||
| fprintf('Speedup vs chunk 128: %.3fx\n',results.speedup_vs_128); | ||
| else | ||
| results.speedup_vs_128 = NaN; | ||
| fprintf('Speedup vs chunk 128: N/A\n'); | ||
| end | ||
|
|
||
| fprintf('Recommended chunk size for rev12 default: %d\n',best_chunk); | ||
|
|
||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,116 @@ | ||
| function [sub_array_agg_check_mc_dBm]=subchunk_agg_check_maxazi_rev12(app,cell_aas_dist_data,array_bs_azi_data,radar_beamwidth,min_azimuth,max_azimuth,base_protection_pts,point_idx,on_list_bs,cell_sim_chunk_idx,rand_seed1,agg_check_reliability,on_full_Pr_dBm,clutter_loss,custom_antenna_pattern,sub_point_idx,varargin) | ||
| %SUBCHUNK_AGG_CHECK_MAXAZI_REV12 | ||
| % Carefully scoped rev12 optimization over rev11: | ||
| % 1) preserve one-time RNG seeding and pre-generated MC random matrices; | ||
| % 2) preserve AZI chunking and allow optional AZI_CHUNK override via varargin{1}; | ||
| % 3) reduce off-axis matrix build overhead with one vectorized nearest-neighbor pass; | ||
| % 4) reduce aggregation temporary allocation pressure via direct mW accumulation. | ||
| % | ||
| % Output contract is unchanged: max aggregate dBm over simulation azimuth for each MC. | ||
|
|
||
| AZI_CHUNK_DEFAULT=128; | ||
| DEBUG_CHECKS=false; | ||
|
|
||
| azi_chunk=AZI_CHUNK_DEFAULT; | ||
| if ~isempty(varargin) | ||
| azi_chunk=varargin{1}; | ||
| end | ||
| azi_chunk=max(1,round(azi_chunk)); | ||
|
|
||
| array_aas_dist_data=cell_aas_dist_data{2}; | ||
| aas_dist_azimuth=cell_aas_dist_data{1}; | ||
| mod_azi_diff_bs=array_bs_azi_data(:,4); | ||
|
|
||
| nn_azi_idx=nearestpoint_app(app,mod_azi_diff_bs,aas_dist_azimuth); | ||
| super_array_bs_eirp_dist=array_aas_dist_data(nn_azi_idx,:); | ||
|
|
||
| [array_sim_azimuth,num_sim_azi]=calc_sim_azimuths_rev3_360_azimuths_app(app,radar_beamwidth,min_azimuth,max_azimuth); | ||
|
|
||
| sim_pt=base_protection_pts(point_idx,:); | ||
| bs_azimuth=azimuth(sim_pt(1),sim_pt(2),on_list_bs(:,1),on_list_bs(:,2)); | ||
|
|
||
| sub_mc_idx=cell_sim_chunk_idx{sub_point_idx}; %#ok<NASGU> | ||
| num_mc_idx=length(sub_mc_idx); | ||
| num_bs=length(bs_azimuth); | ||
| sub_array_agg_check_mc_dBm=NaN(num_mc_idx,1); | ||
|
|
||
| % ------------------------------------------------------------------------- | ||
| % STEP 1: one-time RNG and pre-generated random matrices. | ||
| % ------------------------------------------------------------------------- | ||
| rel_min=min(agg_check_reliability); | ||
| rel_max=max(agg_check_reliability); | ||
|
|
||
| if rel_min==rel_max | ||
| rand_pr_all=repmat(rel_min,num_bs,num_mc_idx); | ||
| rand_eirp_all=rand_pr_all; | ||
| rand_clutter_all=rand_pr_all; | ||
| else | ||
| rng(rand_seed1); | ||
| rel_span=(rel_max-rel_min); | ||
| rand_pr_all=rel_min+rel_span.*rand(num_bs,num_mc_idx); | ||
| rand_eirp_all=rel_min+rel_span.*rand(num_bs,num_mc_idx); | ||
| rand_clutter_all=rel_min+rel_span.*rand(num_bs,num_mc_idx); | ||
| end | ||
|
|
||
| % ------------------------------------------------------------------------- | ||
| % STEP 2: off-axis gain matrix construction (vectorized nearest-neighbor). | ||
| % Semantics are preserved via nearestpoint_app lookup against unique pattern azimuths. | ||
| % ------------------------------------------------------------------------- | ||
| pat_az=mod(custom_antenna_pattern(:,1),360); | ||
| pat_gain=custom_antenna_pattern(:,2); | ||
| [pat_az_unique,ia_unique]=unique(pat_az,'stable'); | ||
| pat_gain_unique=pat_gain(ia_unique); | ||
|
|
||
| rel_az_matrix=mod(bs_azimuth-array_sim_azimuth,360); | ||
| ant_deg_idx_flat=nearestpoint_app(app,rel_az_matrix(:),pat_az_unique); | ||
| off_axis_gain_matrix=reshape(pat_gain_unique(ant_deg_idx_flat),num_bs,num_sim_azi); | ||
|
|
||
| if DEBUG_CHECKS | ||
| if any(~isfinite(off_axis_gain_matrix),'all') | ||
| error('subchunk_agg_check_maxazi_rev12:OffAxisNotFinite', ... | ||
| 'off_axis_gain_matrix contains non-finite values.'); | ||
| end | ||
| end | ||
|
|
||
| % ------------------------------------------------------------------------- | ||
| % STEP 3: RNG-free MC pathloss terms for each MC realization. | ||
| % ------------------------------------------------------------------------- | ||
| sort_monte_carlo_pr_dBm_all=NaN(num_bs,num_mc_idx); | ||
| for loop_idx=1:1:num_mc_idx | ||
| pre_sort_monte_carlo_pr_dBm=monte_carlo_Pr_dBm_rev2_app(app,agg_check_reliability,on_full_Pr_dBm,rand_pr_all(:,loop_idx)); | ||
| rand_norm_eirp=monte_carlo_super_bs_eirp_dist_rev5(app,super_array_bs_eirp_dist,agg_check_reliability,rand_eirp_all(:,loop_idx)); | ||
| monte_carlo_clutter_loss=monte_carlo_clutter_rev3_app(app,agg_check_reliability,clutter_loss,rand_clutter_all(:,loop_idx)); | ||
|
|
||
| sort_monte_carlo_pr_dBm_all(:,loop_idx)=pre_sort_monte_carlo_pr_dBm+rand_norm_eirp-monte_carlo_clutter_loss; | ||
| end | ||
|
|
||
| % ------------------------------------------------------------------------- | ||
| % STEP 4: chunked azimuth aggregation in linear mW domain. | ||
| % max-over-azimuth is tracked in linear domain and converted once per MC. | ||
| % ------------------------------------------------------------------------- | ||
| for loop_idx=1:1:num_mc_idx | ||
| base_mc=sort_monte_carlo_pr_dBm_all(:,loop_idx); | ||
| max_azi_agg_mw=0; | ||
|
|
||
| for azi_start=1:azi_chunk:num_sim_azi | ||
| azi_end=min(azi_start+azi_chunk-1,num_sim_azi); | ||
| chunk_gain=off_axis_gain_matrix(:,azi_start:azi_end); | ||
|
|
||
| if DEBUG_CHECKS | ||
| if any(isnan(chunk_gain),'all') || any(isnan(base_mc),'all') | ||
| error('subchunk_agg_check_maxazi_rev12:NaNInputs','NaN detected before aggregation.'); | ||
| end | ||
| end | ||
|
|
||
| chunk_agg_mw=sum(10.^((base_mc+chunk_gain)./10),1,'omitnan'); | ||
| chunk_max_mw=max(chunk_agg_mw,[],'omitnan'); | ||
|
|
||
| if chunk_max_mw>max_azi_agg_mw | ||
| max_azi_agg_mw=chunk_max_mw; | ||
| end | ||
| end | ||
|
|
||
| sub_array_agg_check_mc_dBm(loop_idx,1)=10.*log10(max(max_azi_agg_mw,realmin('double'))); | ||
| end | ||
|
|
||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,179 @@ | ||
| function results = validate_subchunk_agg_check_maxazi_rev11_rev12_statistical( ... | ||
| app, ... | ||
| cell_aas_dist_data, ... | ||
| array_bs_azi_data, ... | ||
| radar_beamwidth, ... | ||
| min_azimuth, ... | ||
| max_azimuth, ... | ||
| base_protection_pts, ... | ||
| point_idx, ... | ||
| on_list_bs, ... | ||
| cell_sim_chunk_idx, ... | ||
| rand_seed1, ... | ||
| agg_check_reliability, ... | ||
| on_full_Pr_dBm, ... | ||
| clutter_loss, ... | ||
| custom_antenna_pattern, ... | ||
| sub_point_idx) | ||
| %VALIDATE_SUBCHUNK_AGG_CHECK_MAXAZI_REV11_REV12_STATISTICAL | ||
| % Statistical and runtime comparison for rev11 vs rev12 using identical real inputs. | ||
|
|
||
| if exist('subchunk_agg_check_maxazi_rev11','file')~=2 | ||
| error('validate_subchunk_agg_check_maxazi_rev11_rev12_statistical:MissingRev11', ... | ||
| 'subchunk_agg_check_maxazi_rev11.m was not found on MATLAB path.'); | ||
| end | ||
| if exist('subchunk_agg_check_maxazi_rev12','file')~=2 | ||
| error('validate_subchunk_agg_check_maxazi_rev11_rev12_statistical:MissingRev12', ... | ||
| 'subchunk_agg_check_maxazi_rev12.m was not found on MATLAB path.'); | ||
| end | ||
|
|
||
| % Thresholds aligned in style with prior rev10-vs-rev11 validation. | ||
| opts = struct(); | ||
| opts.AziChunkRev11 = 128; | ||
| opts.AziChunkRev12 = 128; | ||
| opts.AbsDiffThreshold_dB = 0.50; | ||
| opts.RelDiffThreshold = 0.05; | ||
| opts.EnableP999 = true; | ||
|
|
||
| fprintf('\n=== REV11 vs REV12 STATISTICAL VALIDATION ===\n'); | ||
| fprintf('AZI_CHUNK rev11: %d\n',opts.AziChunkRev11); | ||
| fprintf('AZI_CHUNK rev12: %d\n',opts.AziChunkRev12); | ||
|
|
||
| % Runtime + output capture for rev11. | ||
| rev11_tic=tic; | ||
| out_rev11=subchunk_agg_check_maxazi_rev11(app,cell_aas_dist_data,array_bs_azi_data, ... | ||
| radar_beamwidth,min_azimuth,max_azimuth,base_protection_pts,point_idx,on_list_bs, ... | ||
| cell_sim_chunk_idx,rand_seed1,agg_check_reliability,on_full_Pr_dBm,clutter_loss, ... | ||
| custom_antenna_pattern,sub_point_idx,opts.AziChunkRev11); | ||
| runtime_rev11=toc(rev11_tic); | ||
|
|
||
| % Runtime + output capture for rev12. | ||
| rev12_tic=tic; | ||
| out_rev12=subchunk_agg_check_maxazi_rev12(app,cell_aas_dist_data,array_bs_azi_data, ... | ||
| radar_beamwidth,min_azimuth,max_azimuth,base_protection_pts,point_idx,on_list_bs, ... | ||
| cell_sim_chunk_idx,rand_seed1,agg_check_reliability,on_full_Pr_dBm,clutter_loss, ... | ||
| custom_antenna_pattern,sub_point_idx,opts.AziChunkRev12); | ||
| runtime_rev12=toc(rev12_tic); | ||
|
|
||
| speedup = runtime_rev11 ./ runtime_rev12; | ||
|
|
||
| x11=out_rev11(:); | ||
| x12=out_rev12(:); | ||
| finite_mask=isfinite(x11) & isfinite(x12); | ||
| x11=x11(finite_mask); | ||
| x12=x12(finite_mask); | ||
|
Comment on lines
+62
to
+64
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The validator drops any sample where either rev11 or rev12 is non-finite before computing drift metrics, so regressions that introduce NaN/Inf (or contract changes in non-finite handling) are silently excluded from Useful? React with 👍 / 👎. |
||
|
|
||
| if isempty(x11) | ||
| error('validate_subchunk_agg_check_maxazi_rev11_rev12_statistical:NoFiniteSamples', ... | ||
| 'No finite paired samples available for statistical comparison.'); | ||
| end | ||
|
|
||
| metrics={'mean','std','min','max','median','p90','p95','p99'}; | ||
| q=[0.90 0.95 0.99]; | ||
|
|
||
| s11.mean=mean(x11,'omitnan'); | ||
| s11.std=std(x11,0,'omitnan'); | ||
| s11.min=min(x11,[],'omitnan'); | ||
| s11.max=max(x11,[],'omitnan'); | ||
| s11.median=median(x11,'omitnan'); | ||
| q11=quantile(x11,q); | ||
| s11.p90=q11(1); s11.p95=q11(2); s11.p99=q11(3); | ||
|
|
||
| s12.mean=mean(x12,'omitnan'); | ||
| s12.std=std(x12,0,'omitnan'); | ||
| s12.min=min(x12,[],'omitnan'); | ||
| s12.max=max(x12,[],'omitnan'); | ||
| s12.median=median(x12,'omitnan'); | ||
| q12=quantile(x12,q); | ||
| s12.p90=q12(1); s12.p95=q12(2); s12.p99=q12(3); | ||
|
|
||
| if opts.EnableP999 && numel(x11)>=1000 | ||
| metrics=[metrics {'p99_9'}]; %#ok<AGROW> | ||
| s11.p99_9=quantile(x11,0.999); | ||
| s12.p99_9=quantile(x12,0.999); | ||
| end | ||
|
|
||
| diff_table=struct(); | ||
| pass_flags=true(1,numel(metrics)); | ||
| for k=1:1:numel(metrics) | ||
| m=metrics{k}; | ||
| v11=s11.(m); | ||
| v12=s12.(m); | ||
| abs_diff=abs(v12-v11); | ||
| rel_diff=abs_diff/max(abs(v11),eps); | ||
| allowed=max(opts.AbsDiffThreshold_dB,opts.RelDiffThreshold*max(abs(v11),1)); | ||
|
|
||
| diff_table.(m).rev11=v11; | ||
| diff_table.(m).rev12=v12; | ||
| diff_table.(m).abs_diff=abs_diff; | ||
| diff_table.(m).rel_diff=rel_diff; | ||
| diff_table.(m).allowed_abs=allowed; | ||
| diff_table.(m).pass=abs_diff<=allowed; | ||
|
|
||
| pass_flags(k)=diff_table.(m).pass; | ||
| end | ||
|
|
||
| tail_fields=intersect({'p95','p99','p99_9'},metrics,'stable'); | ||
| tail_check=struct(); | ||
| tail_pass=true; | ||
| for k=1:1:numel(tail_fields) | ||
| tf=tail_fields{k}; | ||
| abs_diff=diff_table.(tf).abs_diff; | ||
| allowed=diff_table.(tf).allowed_abs; | ||
| tail_check.(tf).abs_diff=abs_diff; | ||
| tail_check.(tf).allowed_abs=allowed; | ||
| tail_check.(tf).pass=abs_diff<=allowed; | ||
| tail_pass=tail_pass && tail_check.(tf).pass; | ||
| end | ||
|
|
||
| overall_pass=all(pass_flags) && tail_pass; | ||
|
|
||
| fprintf('Runtime rev11: %.6f s\n',runtime_rev11); | ||
| fprintf('Runtime rev12: %.6f s\n',runtime_rev12); | ||
| fprintf('Speedup rev11/rev12: %.3fx\n',speedup); | ||
|
|
||
| fprintf('\nMetric comparison (rev12 - rev11):\n'); | ||
| for k=1:1:numel(metrics) | ||
| m=metrics{k}; | ||
| fprintf(' %-7s | rev11=%10.4f | rev12=%10.4f | abs=%.4f | allow=%.4f | %s\n', ... | ||
| m,diff_table.(m).rev11,diff_table.(m).rev12,diff_table.(m).abs_diff, ... | ||
| diff_table.(m).allowed_abs,passfail(diff_table.(m).pass)); | ||
| end | ||
|
|
||
| fprintf('\nUpper-tail checks:\n'); | ||
| for k=1:1:numel(tail_fields) | ||
| tf=tail_fields{k}; | ||
| fprintf(' %-7s | abs=%.4f | allow=%.4f | %s\n',tf,tail_check.(tf).abs_diff, ... | ||
| tail_check.(tf).allowed_abs,passfail(tail_check.(tf).pass)); | ||
| end | ||
|
|
||
| if overall_pass | ||
| fprintf('\nPASS: rev12 is statistically equivalent to rev11 under configured thresholds.\n'); | ||
| else | ||
| fprintf('\nFAIL: rev12 drift exceeded configured thresholds.\n'); | ||
| error('validate_subchunk_agg_check_maxazi_rev11_rev12_statistical:DriftExceeded', ... | ||
| 'Fail-closed: statistical drift exceeded configured thresholds.'); | ||
| end | ||
|
|
||
| results=struct(); | ||
| results.runtime_rev11_s=runtime_rev11; | ||
| results.runtime_rev12_s=runtime_rev12; | ||
| results.speedup_rev11_over_rev12=speedup; | ||
| results.n_samples=numel(x11); | ||
| results.metrics=metrics; | ||
| results.summary_rev11=s11; | ||
| results.summary_rev12=s12; | ||
| results.diffs=diff_table; | ||
| results.upper_tail=tail_check; | ||
| results.thresholds=opts; | ||
| results.pass=overall_pass; | ||
|
|
||
| end | ||
|
|
||
| function txt=passfail(tf) | ||
| if tf | ||
| txt='PASS'; | ||
| else | ||
| txt='FAIL'; | ||
| end | ||
| end | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This conversion now clamps zero aggregate power to
realminbeforelog10, which returns a large finite negative value (~-3076 dBm) instead of the-Infproduced by rev11 (pow2db(0)). In scenarios with no active BS contributions (or all contributions omitted as NaN), this changes the output contract from non-finite to finite and can alter downstream filtering/statistics that useisfiniteto detect no-signal cases.Useful? React with 👍 / 👎.