Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 97 additions & 0 deletions benchmark_subchunk_agg_check_maxazi_rev11_chunk_sweep_real.m
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
116 changes: 116 additions & 0 deletions subchunk_agg_check_maxazi_rev12.m
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')));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve -Inf when aggregate power is zero

This conversion now clamps zero aggregate power to realmin before log10, which returns a large finite negative value (~-3076 dBm) instead of the -Inf produced 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 use isfinite to detect no-signal cases.

Useful? React with 👍 / 👎.

end

end
179 changes: 179 additions & 0 deletions validate_subchunk_agg_check_maxazi_rev11_rev12_statistical.m
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
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Treat non-finite output mismatches as validation failures

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 overall_pass. That undermines the stated fail-closed behavior because the comparison can pass while problematic samples are ignored.

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