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
124 changes: 124 additions & 0 deletions subchunk_agg_check_maxazi_rev10.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
function [sub_array_agg_check_mc_dBm]=subchunk_agg_check_maxazi_rev10(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)
%SUBCHUNK_AGG_CHECK_MAXAZI_REV10 Regression-safe speedup revision.
% rev10 intentionally preserves strict per-iteration RNG behavior from rev9.
% rev10 removes azimuth chunking (when memory is safe) and optimizes aggregation
% without changing random stream semantics.
% rev11 can target RNG/pre-generation/vectorization changes after rev10 validation.

DEBUG_CHECKS=false;

%%%%%%%%%Adding clutter distribution in monte carlo later
%%%%%%%%%%We just have to make a new bs_eirp_dist based on the azimuth
%%%%%%%%%%of the base station antenna offset to the federal point.
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);

%%%%%%%%%Find the azimuth off-axis antenna loss
[nn_azi_idx]=nearestpoint_app(app,mod_azi_diff_bs,aas_dist_azimuth); %%%%%%%Nearest Azimuth Idx
super_array_bs_eirp_dist=array_aas_dist_data(nn_azi_idx, :);

%%%%%%%%%%%%%%%%Calculate the simualation azimuths
[array_sim_azimuth,num_sim_azi]=calc_sim_azimuths_rev3_360_azimuths_app(app,radar_beamwidth,min_azimuth,max_azimuth);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%Calculate Each Base Station 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));

%%%%%%%%%%%%%%Generate MC Iterations and Calculate Move List
sub_mc_idx=cell_sim_chunk_idx{sub_point_idx};
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: Deterministic MC random precompute (seed identity preserved).
% rand_*_all dimensions: [num_bs x num_mc_idx]
% -------------------------------------------------------------------------
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
rand_pr_all=NaN(num_bs,num_mc_idx);
rand_eirp_all=NaN(num_bs,num_mc_idx);
rand_clutter_all=NaN(num_bs,num_mc_idx);

for loop_idx=1:1:num_mc_idx
mc_iter=sub_mc_idx(loop_idx);

rng(rand_seed1+mc_iter); % PR draw identity
rand_pr_all(:,loop_idx)=rand(num_bs,1)*(rel_max-rel_min)+rel_min;

rng(rand_seed1+mc_iter+1); % EIRP draw identity
rand_eirp_all(:,loop_idx)=rand(num_bs,1)*(rel_max-rel_min)+rel_min;

rng(rand_seed1+mc_iter+2); % Clutter draw identity
rand_clutter_all(:,loop_idx)=rand(num_bs,1)*(rel_max-rel_min)+rel_min;
end
end

% -------------------------------------------------------------------------
% STEP 2: Precompute off-axis gain matrix once for all (bs,sim_azimuth).
% off_axis_gain_matrix dimensions: [num_bs x num_sim_azi]
% Nearest-neighbor behavior mirrors rev9 path.
% -------------------------------------------------------------------------
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);

off_axis_gain_matrix=NaN(num_bs,num_sim_azi);
for azimuth_idx=1:1:num_sim_azi
sim_azimuth=array_sim_azimuth(azimuth_idx);
rel_az=mod(bs_azimuth-sim_azimuth,360);
ant_deg_idx=nearestpoint_app(app,rel_az,pat_az_unique);
off_axis_gain_matrix(:,azimuth_idx)=pat_gain_unique(ant_deg_idx);
end

if DEBUG_CHECKS && any(isnan(off_axis_gain_matrix),'all')
error('Inside Agg Check Rev10: NaN Error: off_axis_gain_matrix');
end

% -------------------------------------------------------------------------
% STEP 3/4: Compute MC terms with RNG-free rev helpers.
% -------------------------------------------------------------------------
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 5: Aggregate across full azimuth set in one pass (no azi_chunk loop).
% Keep MC loop for deterministic RNG/regression safety.
% -------------------------------------------------------------------------
for loop_idx=1:1:num_mc_idx
base_mc=sort_monte_carlo_pr_dBm_all(:,loop_idx);
sort_temp_mc_dBm=base_mc+off_axis_gain_matrix;

Comment on lines +103 to +106
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 Reinstate bounded azimuth batching in MC aggregation

This revision computes base_mc + off_axis_gain_matrix for the full azimuth set on every Monte Carlo iteration, which raises peak temporary memory from O(num_bs*azi_chunk) in rev9 to O(num_bs*num_sim_azi). For larger production shapes (many BS rows and fine beamwidths), this can trigger out-of-memory failures on workers even though rev9’s chunked loop completed, so the refactor is not regression-safe under high-load inputs.

Useful? React with 👍 / 👎.

if DEBUG_CHECKS && any(isnan(sort_temp_mc_dBm),'all')
error('Inside Agg Check Rev10: NaN Error: sort_temp_mc_dBm');
end

% Keep numeric path equivalent to rev9 while removing chunk overhead.
binary_sort_mc_watts=db2pow(sort_temp_mc_dBm)/1000;

if DEBUG_CHECKS && any(isnan(binary_sort_mc_watts),'all')
error('Inside Agg Check Rev10: NaN Error: binary_sort_mc_watts');
end

agg_dBm=pow2db(sum(binary_sort_mc_watts,1,"omitnan")*1000);
sub_array_agg_check_mc_dBm(loop_idx,1)=max(agg_dBm);
end

%%%%%%%%%%We can max azimuths -->sub_array_agg_check_mc_dBm=NaN(num_mc_idx,num_sim_azi);

end
158 changes: 158 additions & 0 deletions validate_subchunk_agg_check_maxazi_rev10.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
function results = validate_subchunk_agg_check_maxazi_rev10()
%VALIDATE_SUBCHUNK_AGG_CHECK_MAXAZI_REV10 Deterministic regression harness for rev9 vs rev10.
% This harness validates output equivalence, structural invariants, and runtime.
% NOTE: Intermediate checkpoint comparison inside target functions is not added here,
% because exposing internal arrays (e.g., off_axis_gain_matrix and MC terms) would
% require invasive API/debug-surface changes. This harness stays non-invasive.

rng(20260327,'twister'); % fixed harness seed

cases = { ...
make_case('small', 16, 9, 45, 1, 6), ...
make_case('medium', 64, 41, 10, 1, 9), ...
make_case('large', 96, 73, 5, 1, 12) ...
};

tol = 1e-10;
results = struct('case_name',{},'size_match',{},'column_shape_match',{}, ...
'nan_pattern_match',{},'inf_pattern_match',{},'max_abs_diff',{}, ...
'max_rel_diff',{},'runtime_rev9_s',{},'runtime_rev10_s',{},'speedup',{}, ...
'rev9_reproducible',{},'rev10_reproducible',{},'pass',{});

for k = 1:numel(cases)
c = cases{k};
args = c.args;

out_rev9_a = subchunk_agg_check_maxazi_rev9(args{:});
out_rev10_a = subchunk_agg_check_maxazi_rev10(args{:});

% deterministic reproducibility checks
out_rev9_b = subchunk_agg_check_maxazi_rev9(args{:});
out_rev10_b = subchunk_agg_check_maxazi_rev10(args{:});

size_match = isequal(size(out_rev9_a), size(out_rev10_a));
expected_shape = [numel(c.sub_mc_idx), 1];
column_shape_match = isequal(size(out_rev9_a), expected_shape) && isequal(size(out_rev10_a), expected_shape);
nan_pattern_match = isequal(isnan(out_rev9_a), isnan(out_rev10_a));
inf_pattern_match = isequal(isinf(out_rev9_a), isinf(out_rev10_a));

denom = max(abs(out_rev9_a), 1e-12);
abs_diff = abs(out_rev9_a - out_rev10_a);
rel_diff = abs_diff ./ denom;
max_abs_diff = max(abs_diff,[],'all');
max_rel_diff = max(rel_diff,[],'all');
Comment on lines +40 to +43
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 Mask non-finite entries before computing max diffs

The harness computes abs(out_rev9_a - out_rev10_a) and then takes max(...) over all elements; when both outputs contain matching Inf or NaN entries, operations like Inf-Inf produce NaN, causing max_abs_diff/max_rel_diff to become NaN and the final tolerance check to fail even though nan_pattern_match/inf_pattern_match are true. This yields false FAIL results for valid equivalence cases with non-finite outputs.

Useful? React with 👍 / 👎.


rev9_reproducible = isequaln(out_rev9_a, out_rev9_b);
rev10_reproducible = isequaln(out_rev10_a, out_rev10_b);

% runtime via timeit when available
runtime_rev9_s = NaN;
runtime_rev10_s = NaN;
speedup = NaN;
if exist('timeit','file') == 2
runtime_rev9_s = timeit(@() subchunk_agg_check_maxazi_rev9(args{:}));
runtime_rev10_s = timeit(@() subchunk_agg_check_maxazi_rev10(args{:}));
speedup = runtime_rev9_s / runtime_rev10_s;
else
t = tic; subchunk_agg_check_maxazi_rev9(args{:}); runtime_rev9_s = toc(t);
t = tic; subchunk_agg_check_maxazi_rev10(args{:}); runtime_rev10_s = toc(t);
speedup = runtime_rev9_s / runtime_rev10_s;
end

pass = size_match && column_shape_match && nan_pattern_match && inf_pattern_match && ...
rev9_reproducible && rev10_reproducible && ...
(max_abs_diff <= tol) && (max_rel_diff <= tol);

fprintf('\n--- VALIDATION SUMMARY ---\n');
fprintf('Case: %s\n', c.name);
fprintf('Output Size Match: %d\n', size_match);
fprintf('Column Shape Match: %d\n', column_shape_match);
fprintf('NaN Pattern Match: %d\n', nan_pattern_match);
fprintf('Inf Pattern Match: %d\n', inf_pattern_match);
fprintf('Rev9 Reproducible: %d\n', rev9_reproducible);
fprintf('Rev10 Reproducible: %d\n', rev10_reproducible);
fprintf('Max Abs Diff: %.12g\n', max_abs_diff);
fprintf('Max Rel Diff: %.12g\n', max_rel_diff);
fprintf('Tolerance: %.3e\n', tol);
fprintf('Runtime Rev9: %.6f s\n', runtime_rev9_s);
fprintf('Runtime Rev10: %.6f s\n', runtime_rev10_s);
fprintf('Speedup: %.4fx\n', speedup);
fprintf('Result: %s\n', ternary(pass,'PASS','FAIL'));

if ~pass
error('Validation failed: rev10 does not match rev9 within tolerance (case: %s).', c.name);
end

results(k).case_name = c.name;
results(k).size_match = size_match;
results(k).column_shape_match = column_shape_match;
results(k).nan_pattern_match = nan_pattern_match;
results(k).inf_pattern_match = inf_pattern_match;
results(k).max_abs_diff = max_abs_diff;
results(k).max_rel_diff = max_rel_diff;
results(k).runtime_rev9_s = runtime_rev9_s;
results(k).runtime_rev10_s = runtime_rev10_s;
results(k).speedup = speedup;
results(k).rev9_reproducible = rev9_reproducible;
results(k).rev10_reproducible = rev10_reproducible;
results(k).pass = pass;
end

end

function c = make_case(name, num_bs, num_mc, radar_beamwidth, point_idx, seed_offset)
% Build deterministic synthetic inputs representative of production dimensions.

rng(1000 + seed_offset,'twister');

app = []; %#ok<NASGU>

% reliability grid and distributions (strictly monotonic reliability).
agg_check_reliability = [0.1 0.5 0.9 0.99];
num_rel = numel(agg_check_reliability);

aas_dist_azimuth = (0:5:355).';
array_aas_dist_data = -30 + 10*rand(numel(aas_dist_azimuth), num_rel);
cell_aas_dist_data = {aas_dist_azimuth, array_aas_dist_data};

array_bs_azi_data = zeros(num_bs, 4);
array_bs_azi_data(:,4) = mod(360*rand(num_bs,1), 360);

min_azimuth = 0;
max_azimuth = 355;

base_protection_pts = [37.2 -76.5; 38.9 -77.0; 34.1 -118.2];
on_list_bs = [ ...
25 + 20*rand(num_bs,1), ...
-125 + 55*rand(num_bs,1) ...
];

cell_sim_chunk_idx = {1:num_mc};
rand_seed1 = 12345 + seed_offset;

on_full_Pr_dBm = -140 + 60*rand(num_bs, num_rel);
clutter_loss = 5 + 25*rand(num_bs, num_rel);

pat_az = (0:359).';
pat_gain = -min(abs(pat_az), abs(pat_az-360))/3; % smooth deterministic pattern
custom_antenna_pattern = [pat_az, pat_gain];

sub_point_idx = 1;

args = {[],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};

c = struct();
c.name = name;
c.args = args;
c.sub_mc_idx = cell_sim_chunk_idx{sub_point_idx};
end

function out = ternary(cond, a, b)
if cond
out = a;
else
out = b;
end
end