diff --git a/subchunk_agg_check_maxazi_rev10.m b/subchunk_agg_check_maxazi_rev10.m new file mode 100644 index 0000000..0c90795 --- /dev/null +++ b/subchunk_agg_check_maxazi_rev10.m @@ -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; + + 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 diff --git a/validate_subchunk_agg_check_maxazi_rev10.m b/validate_subchunk_agg_check_maxazi_rev10.m new file mode 100644 index 0000000..fa2aa7d --- /dev/null +++ b/validate_subchunk_agg_check_maxazi_rev10.m @@ -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'); + + 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 + +% 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