diff --git a/agg_check_parfor_chunk_rev10_app.m b/agg_check_parfor_chunk_rev10_app.m new file mode 100644 index 0000000..26dca44 --- /dev/null +++ b/agg_check_parfor_chunk_rev10_app.m @@ -0,0 +1,335 @@ +function [array_agg_check_95,array_agg_check_mc_dBm]=agg_check_parfor_chunk_rev10_app(app,agg_check_reliability,point_idx,sim_number,mc_size,radar_beamwidth,base_protection_pts,mc_percentile,on_list_bs,data_label1,reliability,norm_aas_zero_elevation_data,string_prop_model,single_search_dist,off_idx,min_azimuth,max_azimuth,custom_antenna_pattern,cell_aas_dist_data,cell_sim_data,sim_folder,parallel_flag) + +% REV10 correctness-first orchestration cleanup. +% Strategy-1 scope: remove hard debug pauses from production flow while +% preserving rev9 output contract, chunk scheduling, and checkpoint/disk +% semantics. Assumes monte_carlo_Pr_dBm_rev3_app is already available +% downstream and does not re-implement that dependency in this revision. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%Agg Check Function +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +agg_check_file_name=strcat(string_prop_model,'_array_agg_check_95_',num2str(min(agg_check_reliability)),'_',num2str(max(agg_check_reliability)),'_',num2str(point_idx),'_',num2str(sim_number),'_',num2str(mc_size),'_',num2str(single_search_dist),'km.mat'); +[var_exist_agg_check]=persistent_var_exist_with_corruption(app,agg_check_file_name); + +%%%%%%%%%Need to save the distribution also. +agg_dist_file_name=strcat(string_prop_model,'_array_agg_check_mc_dBm_',num2str(min(agg_check_reliability)),'_',num2str(max(agg_check_reliability)),'_',num2str(point_idx),'_',num2str(sim_number),'_',num2str(mc_size),'_',num2str(single_search_dist),'km.mat'); +[var_exist_agg_dist]=persistent_var_exist_with_corruption(app,agg_dist_file_name); + +if var_exist_agg_check==2 && var_exist_agg_dist==2 + %%%%%%%%%%%load + retry_load=1; + while(retry_load==1) + try + load(agg_check_file_name,'array_agg_check_95') + load(agg_dist_file_name,'array_agg_check_mc_dBm') + retry_load=0; + catch + retry_load=1; + pause(1) + end + end +else + tic; + %point_idx + %%%%%%Persistent Load + %disp_progress(app,strcat('Inside Agg Check Rev1: Line 29: Loading Pathloss')) + file_name_pathloss=strcat(string_prop_model,'_pathloss_',num2str(point_idx),'_',num2str(sim_number),'_',data_label1,'.mat'); + retry_load=1; + while(retry_load==1) + try + load(file_name_pathloss,'pathloss') + retry_load=0; + catch + retry_load=1; + pause(1) + end + end + + file_name_clutter=strcat('P2108_clutter_loss_',num2str(point_idx),'_',num2str(sim_number),'_',data_label1,'.mat'); + retry_load=1; + while(retry_load==1) + try + load(file_name_clutter,'clutter_loss') + retry_load=0; + catch + retry_load=1; + pause(1) + end + end + + %%%%%%%% Cut the reliabilities that we will use for the move list + %disp_progress(app,strcat('Inside Agg Check Rev1: Line 44: Cutting Reliabilities')) + size(pathloss) + [rel_first_idx]=nearestpoint_app(app,min(agg_check_reliability),reliability); + [rel_second_idx]=nearestpoint_app(app,max(agg_check_reliability),reliability); + if strcmp(string_prop_model,'TIREM') + % % % % if TIREM, we wont cut the reliabilites because there are none to cut. + else + pathloss=pathloss(:,[rel_first_idx:rel_second_idx]); + end + size(pathloss) + min(pathloss) + + [pathloss]=fix_inf_pathloss_rev1(app,pathloss); + + %%%%%%%% Cut the reliabilities that we will use for the move list + %size(clutter_loss) + [rel_first_idx]=nearestpoint_app(app,min(agg_check_reliability),reliability); + [rel_second_idx]=nearestpoint_app(app,max(agg_check_reliability),reliability); + clutter_loss=clutter_loss(:,[rel_first_idx:rel_second_idx]); + %size(clutter_loss) + + %%%%%%%%%Cut the pathloss and clutter from those on + pathloss(off_idx,:)=[]; %%%%%%%Cut off_idx + %size(clutter_loss) + clutter_loss(off_idx,:)=[]; %%%%%%%Cut off_idx + %size(clutter_loss) + %size(pathloss) + %size(on_list_bs) + + + %%%%%%%Take into consideration the sector/azimuth off-axis gain + [bs_azi_gain,array_bs_azi_data]=off_axis_gain_bs2fed_rev1(app,base_protection_pts,point_idx,on_list_bs,norm_aas_zero_elevation_data); + %%%%%%array_bs_azi_data --> 1) bs2fed_azimuth 2) sector_azi 3) azi_diff_bs 4) mod_azi_diff_bs 5) bs_azi_gain %%%%%%%%This is the data to save and export to the excel + + tic; + on_full_Pr_dBm=on_list_bs(:,4)-pathloss(:,:)+bs_azi_gain; %%%%%%%%%%%Non-Mitigation EIRP - Pathloss + BS Azi Gain = Power Received at Federal System + toc; + + if any(isnan(bs_azi_gain)) + find(isnan(bs_azi_gain)); + error('agg_check_parfor_chunk_rev10_app:NaNGain','NaN detected in bs_azi_gain.'); + end + + if any(isnan(pathloss)) + find(isnan(pathloss)); + error('agg_check_parfor_chunk_rev10_app:NaNPathloss','NaN detected in pathloss.'); + end + + if any(isnan(on_full_Pr_dBm)) + on_full_Pr_dBm; + error('agg_check_parfor_chunk_rev10_app:NaNPr','NaN detected in on_full_Pr_dBm.'); + end + + + if isempty(on_full_Pr_dBm) + %disp_progress(app,strcat('ERROR PAUSE: Inside Agg Check Rev1: Line 89: Empty on_full_Pr_dBm, cant calculate aggrgate')) + array_agg_check_95=NaN(1,1); + array_agg_check_mc_dBm=NaN(1,1); + %'ERROR PAUSE: Inside Agg Check : Line 89: Empty on_full_Pr_dBm, cant calculate aggrgate' + %pause; + + retry_save=1; + while(retry_save==1) + try + save(agg_dist_file_name,'array_agg_check_mc_dBm') + save(agg_check_file_name,'array_agg_check_95') + retry_save=0; + catch + retry_save=1; + pause(1) + end + end + else + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%Rand Seed1 for MC Iterations + tempx=ceil(rand(1)*mc_size); + tempy=ceil(rand(1)*mc_size); + rand_seed1=tempx+tempy; + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%%%%%%%%%%This is where we break the mc_iter into chunks, like pathloss + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%This is where we define the num_chunks + [num_chunks,cell_sim_chunk_idx,array_rand_chunk_idx]=dynamic_mc_chunks_rev1(app,mc_size); + [hWaitbar_agg_chunks,hWaitbarMsgQueue_agg_chunks]= ParForWaitbarCreateMH_time('Agg Check Chunks: ',num_chunks); %%%%%%% Create ParFor Waitbar, this one covers points and chunks + + + num_chunks + + %%%%'This is where we create the chunks and do a parfor and then stitch together with a for loop' + if parallel_flag==1 + parfor chunk_idx=1:num_chunks %%%%%%%%%Parfor + %parfor_randchunk_aggcheck_rev7(app,agg_check_file_name,agg_dist_file_name,array_rand_chunk_idx,chunk_idx,point_idx,sim_number,data_label1,cell_aas_dist_data,array_bs_azi_data,radar_beamwidth,min_azimuth,max_azimuth,base_protection_pts,on_list_bs,cell_sim_chunk_idx,rand_seed1,agg_check_reliability,on_full_Pr_dBm,clutter_loss,custom_antenna_pattern,parallel_flag); + parfor_randchunk_aggcheck_rev8_claude(app,agg_check_file_name,agg_dist_file_name,array_rand_chunk_idx,chunk_idx,point_idx,sim_number,data_label1,cell_aas_dist_data,array_bs_azi_data,radar_beamwidth,min_azimuth,max_azimuth,base_protection_pts,on_list_bs,cell_sim_chunk_idx,rand_seed1,agg_check_reliability,on_full_Pr_dBm,clutter_loss,custom_antenna_pattern,parallel_flag,single_search_dist); + hWaitbarMsgQueue_agg_chunks.send(0); + end + end + + %%%%'I dont think we need the tf_stop_subchunk' + + %%%%%%%%%Then Assemble with for loop + cell_agg_check=cell(num_chunks,1); + %%tf_stop_subchunk=0; %%%%%%Load it in. + for chunk_idx=1:num_chunks %%%%%%%%%Parfor + sub_point_idx=array_rand_chunk_idx(chunk_idx); + %horzcat(chunk_idx,sub_point_idx) + + % if tf_stop_subchunk==0 + % temp_parallel_flag=0 + % [cell_agg_check{sub_point_idx},tf_stop_subchunk]=parfor_randchunk_aggcheck_rev7(app,agg_check_file_name,agg_dist_file_name,array_rand_chunk_idx,chunk_idx,point_idx,sim_number,data_label1,cell_aas_dist_data,array_bs_azi_data,radar_beamwidth,min_azimuth,max_azimuth,base_protection_pts,on_list_bs,cell_sim_chunk_idx,rand_seed1,agg_check_reliability,on_full_Pr_dBm,clutter_loss,custom_antenna_pattern,temp_parallel_flag); + % end + % tf_stop_subchunk%%%%Once the tf_stop_subchunk + temp_parallel_flag=0; + [sub_array_agg_check_mc_dBm]=parfor_randchunk_aggcheck_rev8_claude(app,agg_check_file_name,agg_dist_file_name,array_rand_chunk_idx,chunk_idx,point_idx,sim_number,data_label1,cell_aas_dist_data,array_bs_azi_data,radar_beamwidth,min_azimuth,max_azimuth,base_protection_pts,on_list_bs,cell_sim_chunk_idx,rand_seed1,agg_check_reliability,on_full_Pr_dBm,clutter_loss,custom_antenna_pattern,temp_parallel_flag,single_search_dist); + cell_agg_check{sub_point_idx}=sub_array_agg_check_mc_dBm; + + % Debug pause removed in rev10 to keep orchestration non-interactive. + + + if parallel_flag==0 + hWaitbarMsgQueue_agg_chunks.send(0);%%%%%%%Decrement the waitbar + end + end + %%%%server_status_rev2(app,tf_server_status) %%%%%%%%%%Send an update after we done all the heavy computation + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + array_agg_check_mc_dBm=vertcat(cell_agg_check{:}); %%%%%%%%Piece it together here. + + if all(isnan(array_agg_check_mc_dBm)) + error('agg_check_parfor_chunk_rev10_app:AllNaN','array_agg_check_mc_dBm is all NaN.'); + else + %%%%%%%%Not NaN, can keep going + %%%%%%%%%%%%%%Generate MC Iterations and Calculate Move List + %array_agg_check_mc_dBm=NaN(mc_size,num_sim_azi); + + [mc_rows,num_azi]=size(array_agg_check_mc_dBm); %#ok + if num_azi>1 + error('agg_check_parfor_chunk_rev10_app:UnsupportedAziColumns','num_azi>1 not yet supported in this orchestration path.'); + end + + size(array_agg_check_mc_dBm) + array_agg_check_95=prctile(sort(array_agg_check_mc_dBm),mc_percentile); + [f_y,x_dB]=ecdf(array_agg_check_mc_dBm); + %horzcat(x_dB,f_y) + + + %%%%%%%%%%Find the secondary I/N and Percentiles and the + + data_header=cell_sim_data(1,:)'; + in1_idx=find(matches(data_header,'in_ratio')); + label_idx=find(matches(data_header,'data_label1')); + row_folder_idx=find(matches(cell_sim_data(:,label_idx),sim_folder)); + in_ratio1=cell_sim_data{row_folder_idx,in1_idx}; + + %%%%%Need the secondary, if they are there + in2_idx=find(matches(data_header,'second_in_ratio')); + per2_idx=find(matches(data_header,'second_mc_percentile')); + + threshold_idx=find(matches(data_header,'dpa_threshold')); + radar_threshold=cell_sim_data{row_folder_idx,threshold_idx}; + + + %%%%%%%%%%This is the zero dB shift. + zero_dB=in_ratio1-radar_threshold; + per_first_in=array_agg_check_95+zero_dB; + + if ~isempty(in2_idx) + in_ratio2=cell_sim_data{row_folder_idx,in2_idx}; + else + in_ratio2=NaN(1,1); + end + if ~isempty(per2_idx) + per2=cell_sim_data{row_folder_idx,per2_idx}; + + %%%%%%Find the second percentile also + array_agg_check_second=prctile(sort(array_agg_check_mc_dBm),per2); + per_second_in=array_agg_check_second+zero_dB; + else + per2=NaN(1,1); + per_second_in=NaN(1,1); + end + + f2=figure; + hold on; + plot(x_dB+zero_dB,f_y,':b','LineWidth',3) + yline(mc_percentile/100,'--k','LineWidth',1) + xline(in_ratio1,'-r','LineWidth',2) + xline(per_first_in,':r','LineWidth',2) + xline(in_ratio2,'-g','LineWidth',2) + yline(per2/100,'--k','LineWidth',1) + xline(per_second_in,':g','LineWidth',2) + plot(x_dB+zero_dB,f_y,':b','LineWidth',3) + title('Aggregate CDF') + xlabel('I/N [dB]') + ylabel('Cumulative Probability') + grid on; + pause(0.1) + filename1=strcat('AggCheckCDF_',num2str(point_idx),'_',num2str(single_search_dist),'km.png'); + retry_save=1; + while(retry_save==1) + try + saveas(gcf,char(filename1)) + retry_save=0; + catch + retry_save=1; + pause(1) + end + end + pause(0.1); + close(f2) + + + %%%%%Save master_turn_off_idx, Persistent Save + %disp_progress(app,strcat('Inside Agg Check Rev1: Line 194: Saving array_agg_check_95')) + retry_save=1; + while(retry_save==1) + try + save(agg_dist_file_name,'array_agg_check_mc_dBm') + save(agg_check_file_name,'array_agg_check_95') + retry_save=0; + catch + retry_save=1; + pause(1) + end + end + + end + end + toc; + + % Hard debug stop removed in rev10; continue to cleanup and return. + + %%%%'delete the chunks before leaving this function' + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%'This is where we then clean up the single point' + %%%%%%%%%%%%Double check that it is there. + tf_file_check_loop=1; + while(tf_file_check_loop==1) + try + [var_exist1]=persistent_var_exist_with_corruption(app,agg_dist_file_name); + [var_exist2]=persistent_var_exist_with_corruption(app,agg_check_file_name); + pause(0.1); + catch + var_exist1=0; + var_exist2=0; + pause(0.1) + end + if var_exist1==2 && var_exist2==2 + tf_file_check_loop=0; + else + tf_file_check_loop=1; + pause(10) + end + end + + if var_exist1==2 && var_exist2==2 + %%%%%%%%%Loop for deleting + for sub_point_idx=1:num_chunks + %%file_name_agg_check_chunk=strcat('sub_',num2str(sub_point_idx),'_array_agg_check_mc_dBm_',num2str(point_idx),'_',num2str(sim_number),'_',data_label1,'.mat'); + file_name_agg_check_chunk=strcat('sub_',num2str(sub_point_idx),'_array_agg_check_mc_dBm_',num2str(point_idx),'_',num2str(sim_number),'_',data_label1,'_',num2str(single_search_dist),'km.mat'); + persistent_delete_rev1(app,file_name_agg_check_chunk) + end + end + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%End of clean up + + %%%'Need to clean up the waittimer before leaving the function' + try + delete(hWaitbarMsgQueue_agg_chunks); + close(hWaitbar_agg_chunks); + catch + end + +end \ No newline at end of file diff --git a/profile_agg_check_parfor_chunk_rev9_real.m b/profile_agg_check_parfor_chunk_rev9_real.m new file mode 100644 index 0000000..560732f --- /dev/null +++ b/profile_agg_check_parfor_chunk_rev9_real.m @@ -0,0 +1,148 @@ +function results = profile_agg_check_parfor_chunk_rev9_real(app,agg_check_reliability,point_idx,sim_number,mc_size,radar_beamwidth,base_protection_pts,mc_percentile,on_list_bs,data_label1,reliability,norm_aas_zero_elevation_data,string_prop_model,single_search_dist,off_idx,min_azimuth,max_azimuth,custom_antenna_pattern,cell_aas_dist_data,cell_sim_data,sim_folder,parallel_flag,varargin) +% Profile agg_check_parfor_chunk_rev9_app on real inputs. +% NOTE: This path depends on downstream functions (including +% monte_carlo_Pr_dBm_rev3_app once wired in the active compute stack). + +opts = parse_opts(varargin{:}); +must_exist('agg_check_parfor_chunk_rev9_app','MissingRev9'); + +fprintf('\n=== PROFILE agg_check_parfor_chunk_rev9_app (real inputs) ===\n'); + +profile off; +profile clear; +if opts.enable_detail_builtin + profile('-memory','off','-detail','builtin'); +end +profile on; + +wall_tic = tic; +[out95,outmc] = agg_check_parfor_chunk_rev9_app(app,agg_check_reliability,point_idx,sim_number,mc_size,radar_beamwidth,base_protection_pts,mc_percentile,on_list_bs,data_label1,reliability,norm_aas_zero_elevation_data,string_prop_model,single_search_dist,off_idx,min_azimuth,max_azimuth,custom_antenna_pattern,cell_aas_dist_data,cell_sim_data,sim_folder,parallel_flag); %#ok +wall_runtime_s = toc(wall_tic); + +profile off; +pinfo = profile('info'); +if ~isfield(pinfo,'FunctionTable') || isempty(pinfo.FunctionTable) + error('profile_agg_check_parfor_chunk_rev9_real:EmptyProfile','Profiler returned no rows.'); +end + +tbl = build_profile_table(pinfo.FunctionTable); +[~,idx_total] = sort(tbl.TotalTime_s,'descend','MissingPlacement','last'); +top_n = min(opts.top_n,height(tbl)); +top_total = tbl(idx_total(1:top_n),:); + +fprintf('\nTop contributors by total time:\n'); +disp(top_total); + +% Bucketized attribution for requested dominance statement. +orchestration_patterns = {'agg_check_parfor_chunk_rev9_app','parfor_randchunk_aggcheck_rev8_claude', ... + 'ParForWaitbar','dynamic_mc_chunks_rev1','off_axis_gain_bs2fed_rev1'}; + +downstream_patterns = {'subchunk_agg_check_maxazi_','monte_carlo_','interp1','nearestpoint_app'}; + +disk_patterns = {'save','load','persistent_var_exist_with_corruption','persistent_delete_rev1','load_variable_with_retry'}; + +orchestration = summarize_pattern_group(tbl,orchestration_patterns,wall_runtime_s); +downstream = summarize_pattern_group(tbl,downstream_patterns,wall_runtime_s); +diskio = summarize_pattern_group(tbl,disk_patterns,wall_runtime_s); + +bucket_tbl = table( ... + {'orchestration/parfor-wrapper';'downstream-subchunk/helper-compute';'checkpoint/disk-activity'}, ... + [orchestration.total_time_s;downstream.total_time_s;diskio.total_time_s], ... + [orchestration.pct_of_wall;downstream.pct_of_wall;diskio.pct_of_wall], ... + 'VariableNames',{'Bucket','TotalTime_s','PctOfWall'}); + +[~,dom_idx] = max(bucket_tbl.TotalTime_s); +dominant_bucket = bucket_tbl.Bucket{dom_idx}; + +fprintf('\nSummary table (requested dominance categories):\n'); +disp(bucket_tbl); +fprintf('Dominant runtime bucket: %s\n', dominant_bucket); + +results = struct(); +results.runtime_s = wall_runtime_s; +results.top_contributors = top_total; +results.bucket_table = bucket_tbl; +results.dominant_bucket = dominant_bucket; +results.output_size_95 = size(out95); +results.output_size_mc = size(outmc); +results.full_profile_table = tbl; +results.notes = 'Assumes downstream path availability (including monte_carlo_Pr_dBm_rev3_app when active in compute stack).'; + +end + +function opts = parse_opts(varargin) +opts = struct('top_n',20,'enable_detail_builtin',true); +if isempty(varargin), return; end +if mod(numel(varargin),2) ~= 0 + error('parse_opts:NameValue','Optional args must be name/value pairs.'); +end +for i = 1:2:numel(varargin) + k = lower(string(varargin{i})); + v = varargin{i+1}; + switch k + case "top_n" + opts.top_n = v; + case "enable_detail_builtin" + opts.enable_detail_builtin = logical(v); + otherwise + error('parse_opts:UnknownOption','Unknown option: %s',k); + end +end +end + +function tbl = build_profile_table(ft) +n = numel(ft); +name_col = cell(n,1); +total_col = zeros(n,1); +self_col = zeros(n,1); +calls_col = zeros(n,1); +for i = 1:n + name_col{i} = safe_get(ft(i),{'FunctionName','CompleteName','FileName'},''); + total_col(i) = safe_get(ft(i),{'TotalTime'},NaN); + self_col(i) = safe_get(ft(i),{'SelfTime'},NaN); + calls_col(i) = safe_get(ft(i),{'NumCalls'},NaN); +end +tbl = table(name_col,total_col,self_col,calls_col, ... + 'VariableNames',{'Function','TotalTime_s','SelfTime_s','NumCalls'}); +end + +function s = summarize_pattern_group(tbl,patterns,wall_runtime_s) +rows = false(height(tbl),1); +for p = 1:numel(patterns) + rows = rows | match_rows(tbl,patterns{p}); +end +s = struct(); +s.patterns = patterns; +s.num_rows = nnz(rows); +s.total_time_s = sum(tbl.TotalTime_s(rows),'omitnan'); +s.self_time_s = sum(tbl.SelfTime_s(rows),'omitnan'); +s.calls = sum(tbl.NumCalls(rows),'omitnan'); +s.pct_of_wall = 100*s.total_time_s/max(wall_runtime_s,eps); +s.matches = tbl.Function(rows); +end + +function rows = match_rows(tbl,pattern) +rows = false(height(tbl),1); +for i = 1:height(tbl) + if contains(tbl.Function{i},pattern,'IgnoreCase',true) + rows(i) = true; + end +end +end + +function val = safe_get(s,keys,default_val) +val = default_val; +for k = 1:numel(keys) + if isfield(s,keys{k}) + val = s.(keys{k}); + return; + end +end +end + +function must_exist(fname,errid) +if exist(fname,'file')~=2 + error(['profile_agg_check_parfor_chunk_rev9_real:' errid], ... + '%s.m was not found on MATLAB path.',fname); +end +end diff --git a/validate_agg_check_parfor_chunk_rev9_rev10_real.m b/validate_agg_check_parfor_chunk_rev9_rev10_real.m new file mode 100644 index 0000000..17c7554 --- /dev/null +++ b/validate_agg_check_parfor_chunk_rev9_rev10_real.m @@ -0,0 +1,171 @@ +function results = validate_agg_check_parfor_chunk_rev9_rev10_real(app,agg_check_reliability,point_idx,sim_number,mc_size,radar_beamwidth,base_protection_pts,mc_percentile,on_list_bs,data_label1,reliability,norm_aas_zero_elevation_data,string_prop_model,single_search_dist,off_idx,min_azimuth,max_azimuth,custom_antenna_pattern,cell_aas_dist_data,cell_sim_data,sim_folder,parallel_flag,varargin) +% Validate rev9 vs rev10 on identical real inputs. +% NOTE: rev10 assumes monte_carlo_Pr_dBm_rev3_app is present downstream. +% Validation depends on that downstream path being available in the runtime. + +opts = parse_opts(varargin{:}); + +fprintf('\n=== VALIDATE agg_check_parfor_chunk rev9 vs rev10 (real inputs) ===\n'); +fprintf('Assumption: monte_carlo_Pr_dBm_rev3_app exists and is available on path.\n'); + +% Run rev9 +tic; +[out95_rev9,outmc_rev9] = agg_check_parfor_chunk_rev9_app(app,agg_check_reliability,point_idx,sim_number,mc_size,radar_beamwidth,base_protection_pts,mc_percentile,on_list_bs,data_label1,reliability,norm_aas_zero_elevation_data,string_prop_model,single_search_dist,off_idx,min_azimuth,max_azimuth,custom_antenna_pattern,cell_aas_dist_data,cell_sim_data,sim_folder,parallel_flag); +runtime_rev9 = toc; + +% Run rev10 +tic; +[out95_rev10,outmc_rev10] = agg_check_parfor_chunk_rev10_app(app,agg_check_reliability,point_idx,sim_number,mc_size,radar_beamwidth,base_protection_pts,mc_percentile,on_list_bs,data_label1,reliability,norm_aas_zero_elevation_data,string_prop_model,single_search_dist,off_idx,min_azimuth,max_azimuth,custom_antenna_pattern,cell_aas_dist_data,cell_sim_data,sim_folder,parallel_flag); +runtime_rev10 = toc; + +cmp_95 = deep_compare(out95_rev9,out95_rev10,opts.abs_tol,opts.rel_tol); +cmp_mc = deep_compare(outmc_rev9,outmc_rev10,opts.abs_tol,opts.rel_tol); + +max_abs_diff = max(cmp_95.max_abs_diff,cmp_mc.max_abs_diff); +max_rel_diff = max(cmp_95.max_rel_diff,cmp_mc.max_rel_diff); +all_equal = cmp_95.equal_within_tol && cmp_mc.equal_within_tol; + +if runtime_rev10 > 0 + speedup = runtime_rev9 / runtime_rev10; +else + speedup = NaN; +end + +pass = all_equal && (max_abs_diff <= opts.abs_tol) && (max_rel_diff <= opts.rel_tol); + +results = struct(); +results.runtime_rev9 = runtime_rev9; +results.runtime_rev10 = runtime_rev10; +results.speedup = speedup; +results.max_abs_diff = max_abs_diff; +results.max_rel_diff = max_rel_diff; +results.pass = pass; +results.abs_tol = opts.abs_tol; +results.rel_tol = opts.rel_tol; +results.compare_95 = cmp_95; +results.compare_mc = cmp_mc; + +fprintf('runtime_rev9 : %.6f s\n', runtime_rev9); +fprintf('runtime_rev10: %.6f s\n', runtime_rev10); +fprintf('speedup : %.6fx (rev9/rev10)\n', speedup); +fprintf('max abs diff : %.6g\n', max_abs_diff); +fprintf('max rel diff : %.6g\n', max_rel_diff); +fprintf('thresholds : abs<=%.3g, rel<=%.3g\n', opts.abs_tol, opts.rel_tol); +if pass + fprintf('RESULT : PASS\n'); +else + fprintf('RESULT : FAIL\n'); + error('validate_agg_check_parfor_chunk_rev9_rev10_real:DriftExceeded', ... + 'rev9/rev10 drift exceeded thresholds (fail-closed).'); +end + +end + +function opts = parse_opts(varargin) +opts = struct('abs_tol',1e-10,'rel_tol',1e-10); +if isempty(varargin) + return; +end +if mod(numel(varargin),2) ~= 0 + error('parse_opts:NameValue','Optional args must be name/value pairs.'); +end +for i = 1:2:numel(varargin) + k = lower(string(varargin{i})); + v = varargin{i+1}; + switch k + case "abs_tol" + opts.abs_tol = v; + case "rel_tol" + opts.rel_tol = v; + otherwise + error('parse_opts:UnknownOption','Unknown option: %s',k); + end +end +end + +function cmp = deep_compare(a,b,abs_tol,rel_tol) +cmp = struct('class_equal',strcmp(class(a),class(b)), ... + 'size_equal',isequal(size(a),size(b)), ... + 'nan_pattern_equal',true, ... + 'inf_pattern_equal',true, ... + 'max_abs_diff',0, ... + 'max_rel_diff',0, ... + 'equal_within_tol',true); + +if ~(cmp.class_equal && cmp.size_equal) + cmp.equal_within_tol = false; + cmp.max_abs_diff = Inf; + cmp.max_rel_diff = Inf; + return; +end + +if isnumeric(a) || islogical(a) + cmp = compare_numeric(a,b,abs_tol,rel_tol,cmp); +elseif iscell(a) + for i = 1:numel(a) + child = deep_compare(a{i},b{i},abs_tol,rel_tol); + cmp = merge_cmp(cmp,child,abs_tol,rel_tol); + end +elseif isstruct(a) + fa = fieldnames(a); + fb = fieldnames(b); + if ~isequal(sort(fa),sort(fb)) + cmp.equal_within_tol = false; + cmp.max_abs_diff = Inf; + cmp.max_rel_diff = Inf; + return; + end + for k = 1:numel(a) + for f = 1:numel(fa) + child = deep_compare(a(k).(fa{f}),b(k).(fa{f}),abs_tol,rel_tol); + cmp = merge_cmp(cmp,child,abs_tol,rel_tol); + end + end +else + % Fallback for other MATLAB types. + if ~isequaln(a,b) + cmp.equal_within_tol = false; + cmp.max_abs_diff = Inf; + cmp.max_rel_diff = Inf; + end +end +end + +function cmp = compare_numeric(a,b,abs_tol,rel_tol,cmp) +if isempty(a) && isempty(b) + return; +end +na = isnan(a); nb = isnan(b); +ia = isinf(a); ib = isinf(b); +cmp.nan_pattern_equal = isequal(na,nb); +cmp.inf_pattern_equal = isequal(ia,ib); +if ~(cmp.nan_pattern_equal && cmp.inf_pattern_equal) + cmp.equal_within_tol = false; + cmp.max_abs_diff = Inf; + cmp.max_rel_diff = Inf; + return; +end +finite_mask = isfinite(a) & isfinite(b); +if ~any(finite_mask(:)) + return; +end +da = abs(a(finite_mask) - b(finite_mask)); +scale = max(abs(a(finite_mask)), abs(b(finite_mask))); +dr = da ./ max(scale, eps); +cmp.max_abs_diff = max(da); +cmp.max_rel_diff = max(dr); +cmp.equal_within_tol = (cmp.max_abs_diff <= abs_tol) && (cmp.max_rel_diff <= rel_tol); +end + +function out = merge_cmp(a,b,abs_tol,rel_tol) +out = a; +out.class_equal = a.class_equal && b.class_equal; +out.size_equal = a.size_equal && b.size_equal; +out.nan_pattern_equal = a.nan_pattern_equal && b.nan_pattern_equal; +out.inf_pattern_equal = a.inf_pattern_equal && b.inf_pattern_equal; +out.max_abs_diff = max(a.max_abs_diff,b.max_abs_diff); +out.max_rel_diff = max(a.max_rel_diff,b.max_rel_diff); +out.equal_within_tol = out.class_equal && out.size_equal && out.nan_pattern_equal && ... + out.inf_pattern_equal && (out.max_abs_diff <= abs_tol) && (out.max_rel_diff <= rel_tol) && ... + a.equal_within_tol && b.equal_within_tol; +end