rbmatlab 0.10.01
general/+Postprocess/+StochasticAssessment/Output.m
00001 classdef Output < handle
00002   % result class for computations executed by an
00003   % Postprocess.StochasticAssessment.Assessment object.
00004   %
00005   % This class provides methods to transform and visualize the gathered data
00006   % for publication in LaTeX/Tikz documents.
00007 
00008   properties
00009 
00010     % values computed by Postprocess.StochasticAssessment.Assessment
00011     %
00012     % Usually this is a cell array of structure arrays. For cells correspond to
00013     % the @ref Assessment.rsamples "rsamples" and the structure array
00014     % corresponds to the test samples given by @ref Assessment.M_test "M_test".
00015     values;
00016 
00017     % cell array of all condition numbers of reduced simulaton system matrices.
00018     rd_conds;
00019 
00020     rtime_est = {};     % time for the computation of the error estimator. By default this is not set.
00021 
00022     % the object of type Assessment with which the values are generated
00023     %
00024     % @todo Maybe we should cache this, too...
00025     assessment;
00026 
00027     % maxmimum error values to be plotted. Above this values error display is
00028     % cropped.
00029     %
00030     % @default = 1;
00031     error_bound = 1;
00032 
00033     % maxmimum estimator values to be plotted. Above this values error display
00034     % is cropped.
00035     %
00036     % @default = 100;
00037     estimator_bound = 100;
00038 
00039     % define stability-region as error being smaller than sqrt(diffmax * area),
00040     % e.g. diffmax = 4, area = 2e-7
00041 
00042     % stability region
00043     %
00044     % If the averaged error exceeds this value, the visualization is cropped
00045     % here. This happens especially for small reduced basis sizes. (Default =
00046     % '1e-2')
00047     stab_limit = 1e-2;
00048 
00049   end
00050 
00051   methods
00052 
00053     function output = Output(values, assessment)
00054       % function output = Output(values, assessment)
00055       % Constructor
00056       %
00057       % Parameters:
00058       %  values:     the structure with the gathered data by the
00059       %              Postprocess.StochasticAssessment.Assessment object
00060       %  assessment: the generating object of type Postprocess.StochasticAssessment.Assessment
00061 
00062       output.values     = values;
00063       output.assessment = assessment;
00064 
00065     end
00066 
00067     function plot_landscape(this)
00068       %function plot_landscape(this)
00069       % plots a two dimensional error landscape in case of two dimensional
00070       % @ref Assessment.plot_fields "plot_fields"
00071       %
00072       % Used attributes:
00073       %  - #error_bound and
00074       %  - #stab_limit
00075 
00076       errs = this.get_slice('max_err');
00077       i = logical(errs>this.error_bound);
00078       errs(i) = this.error_bound;
00079       i = logical(isnan(errs));
00080       errs(i) = this.error_bound;
00081       reshape(errs, size(this.values));
00082       C = ones(size(errs));
00083       i = logical(errs<this.stab_limit);
00084       C(i) = 2;
00085 
00086       figure;
00087       if length(this.assessment.samples) == 2
00088         if isempty(find(C==1,1)); % i.e. all stable
00089           surf(this.assessment.samples{1},this.assessment.tsamples{2},errs');
00090         else % some not stable
00091           surf(this.assessment.samples{1},this.assessment.tsamples{2},errs',C');
00092         end;
00093         shading interp;
00094         % figure, pcolor(Ms, Ns,C);
00095         set(gca,'Zscale','log');
00096         xlabel(this.assessment.pf_descr{1});
00097         ylabel(this.assessment.pf_descr{2});
00098         zlabel(this.assessment.error_label);
00099       else
00100         plot(this.assessment.rsamples, errs');
00101         xlabel(this.assessment.pf_descr{1});
00102         ylabel(this.assessment.error_label);
00103         set(gca,'Yscale','log');
00104       end
00105       run_name = strrep(this.assessment.run_name,'_',' ');
00106       title([run_name,' L-infty([0,T],L2) error']);
00107 
00108       figure;
00109       if length(this.assessment.samples) == 2
00110         surf(this.assessment.samples{1}, this.assessment.samples{2}, real(this.get_slice('max_err_inds')));
00111         xlabel(this.assessment.pf_descr{1});
00112         ylabel(this.assessment.pf_descr{2});
00113         zlabel('time index');
00114       else
00115         plot(this.assessment.samples, this.get_slice('max_err_inds'));
00116         xlabel(this.assessment.pf_descr{1});
00117         zlabel('time index');
00118       end
00119 
00120       title('time index of maximum l2-error');
00121       %set(gca,'Zscale','log');
00122     end
00123 
00124     function print_table(this, datafile)
00125       % function print_table(this, datafile)
00126       % prints out a CSV table with gathered data.
00127       %
00128       % An example for a pgfplotstable graphic that can interpret the table output
00129       % is shown in the following code snippet: 
00130       % @code
00131       % \pgfplotstabletypeset[
00132       %   font={\footnotesize},
00133       %   columns={dim1,dim2,{time_avg},{err_max},{offtime_total}},
00134       %   columns/{dim1}/.style={
00135       %     column name={N},
00136       %     postproc cell content/.append code={%
00137       %       \ifnum\pgfplotstablerow=0
00138       %       \pgfkeyssetvalue{/pgfplots/table/@cell content}{$H={}$#2}%
00139       %       \fi
00140       %     }%
00141       %   },
00142       %   columns/{dim2}/.style={
00143       %     column name={M},
00144       %     postproc cell content/.append code={%
00145       %       \ifnum\pgfplotstablerow=0
00146       %       \pgfkeyssetvalue{/pgfplots/table/@cell content}{\ensuremath{-}}%
00147       %       \fi
00148       %     },
00149       %   },
00150       %   columns/{time_avg}/.style={
00151       %     column name={\o-run--time[s]}
00152       %   },
00153       %   columns/{err_max}/.style={
00154       %     column name={max. error}
00155       %   },
00156       %   columns/{offtime_total}/.style={
00157       %     column name={offline time[h]},
00158       %     divide by=3600
00159       %   },
00160       %   every even row/.style={
00161       %     before row={\rowcolor[gray]{0.9}}},
00162       %   every head row/.style={
00163       %     before row=\toprule,after row=\midrule},
00164       %   every last row/.style={
00165       %     after row=\bottomrule},
00166       %   display columns/2/.style={dec sep align},
00167       %   display columns/3/.style={zerofill,precision=2},
00168       %   empty cells with={\ensuremath{-}}
00169       % ]{table.dat};
00170       % @endcode
00171       %
00172       % Parameters:
00173       %  datafile: a string specifying the path and filename of the data table to be stored
00174       %
00175       % This function prints out a table with data for the average, min and max
00176       % values of
00177       %  - reduced simulation time,
00178       %  - reduced simulation reconstruction time,
00179       %  - reduced simulation error estimator computation time,
00180       %  - maximum error estimator and the
00181       %  - maximum "true" error
00182       % Furthermore, the offline time is printed out split up into
00183       %  - the total time spent in the offline phase,
00184       %  - the time spent for (pre-)computation of detailed simulations
00185       %  - the time spent in the EI-greedy algorithm
00186       %  - the time spent in the reduced basis generation algorithm
00187       ratime    = this.reduce_slice(@mean, 'rtime');
00188       rmtime    = this.reduce_slice(@min, 'rtime');
00189       rMtime    = this.reduce_slice(@max, 'rtime');
00190       rratime   = this.reduce_slice(@mean, 'rrtime');
00191       rrmtime   = this.reduce_slice(@min, 'rrtime');
00192       rrMtime   = this.reduce_slice(@max, 'rrtime');
00193       restatime = this.reduce_slice(@mean, 'rtime_est');
00194       restmtime = this.reduce_slice(@min, 'rtime_est');
00195       restMtime = this.reduce_slice(@max, 'rtime_est');
00196       etaa      = this.reduce_slice(@mean, 'max_Delta');
00197       etam      = this.reduce_slice(@min, 'max_Delta');
00198       etaM      = this.reduce_slice(@max, 'max_Delta');
00199       erra      = this.reduce_slice(@mean, 'max_err');
00200       errm      = this.reduce_slice(@min, 'max_err');
00201       errM      = this.reduce_slice(@max, 'max_err');
00202 
00203       dimension = ones(length(ratime),2);
00204       ratio = ones(length(ratime),1);
00205       simulation = cell(length(ratime),1);
00206       offt  = ones(length(ratime), 4);
00207       asm = this.assessment;
00208       rbdd = get_by_description(asm.detailed_data, 'rb');
00209 
00210       for comb = 1:length(asm.rsamples)
00211         pf_val = asm.rsamples(:,comb);
00212         for i = 1:length(pf_val)
00213           asm.update_rbmodel(asm.plot_fields{i}, asm.rsamples(i, comb));
00214         end
00215         N = asm.rbmodel.N;
00216         M = asm.rbmodel.M;
00217         simulation{comb} = 'Reduced';
00218         dimension(comb,1) = N;
00219         dimension(comb, 2) = M;
00220         if length(pf_val) == 1
00221           ratio(comb) = asm.rsamples(comb);
00222         else
00223           ratio(comb) = comb;
00224         end
00225         [offt(comb,1), offt(comb,2), offt(comb,3)] = offtime(asm.detailed_data, asm.rbmodel);
00226       end
00227       if offt(1,2) < 50
00228         offt(:,1) = 90;
00229       elseif offt(1,2) < 65
00230         offt(:,1) = 117;
00231       elseif offt(1,2) < 1300
00232         offt(:,1) = 887;
00233       else
00234         offt(:,1) = 1394;
00235       end
00236       offt(:,4) = sum(offt, 2);
00237       datime    = this.reduce_slice(@mean, 'dtime', [], 1);
00238       dmtime    = this.reduce_slice(@min, 'dtime', [], 1);
00239       dMtime    = this.reduce_slice(@max, 'dtime', [], 1);
00240       Hsize = asm.detailed_data.model_data.grid.nelements;
00241       ddimension = Hsize;
00242       dratio = -1;
00243       print_datatable(datafile,...
00244         {...
00245          'ratio', 'dim1', 'dim2', ...
00246          'time_avg', 'time_max', 'time_min', 'rec_time_avg', 'rec_time_min', 'rec_time_max', ...
00247          'est_time_avg', 'est_time_min', 'est_time_max', 'eta_avg', 'eta_min', 'eta_max', ...
00248          'err_avg', 'err_min', 'err_max', ...,
00249          'offtime_dsims', 'offtime_ei', 'offtime_rb', 'offtime_total' ...
00250         }, ...
00251         [...
00252          dratio, 0, ddimension, ...
00253          datime, dmtime, dMtime, -1, -1 , -1, ...
00254          -1, -1, -1, 0, 0, 0, ...
00255          0, 0, 0, ...
00256          0, 0, 0, 0; ...
00257          ratio(:), dimension, ...
00258          ratime(:), rmtime(:), rMtime(:), rratime(:), rrmtime(:), rrMtime(:), ...
00259          restatime(:), restmtime(:), restMtime(:), etaa(:), etam(:), etaM(:), ...
00260          erra(:), errm(:), errM(:), ...
00261          offt...
00262         ]');
00263 
00264     end
00265 
00266     function print_landscape(this, datafile, fieldname)
00267       % function print_landscape(this, datafile, fieldname)
00268       % equivalent to the plot_landscape() function put prints out a table
00269       % which can be interpreted e.g. by pgfplots/Tikz.
00270       %
00271       % An example for a pgfplots graphics that can interpret the table output
00272       % is shown in the following code snippet: 
00273       % @code
00274       % \begin{axis}[zmode=log,log basis z=10,axis on top,
00275       %              view={-50}{-30}, axis lines=box,
00276       %              xlabel={$N$}, ylabel={$M$},
00277       %              zlabel={$\eta_{N,M,M'}$}]
00278       %  \addplot3[surf]
00279       %  table[x=N, y=M, z=error] {error_landscape.dat};
00280       % @endcode
00281       %
00282       % Parameters:
00283       %  datafile:  a string specifying the path and filename of the data table to be stored
00284       %  fieldname: the fieldname in the #values attribute that shall be
00285       %             plotted @default 'max_err'
00286       if nargin <=2
00287         fieldname = 'max_err';
00288       end
00289       errs = this.reduce_slice(@max, fieldname);
00290       i = logical(errs>this.error_bound);
00291       errs(i) = this.error_bound;
00292       i = logical(isnan(errs));
00293       errs(i) = this.error_bound;
00294 
00295       rsamples    = this.assessment.rsamples;
00296       blocklength = length(this.assessment.samples{1});
00297 
00298       print_datatable(datafile, [this.assessment.plot_fields, {'error'}], ...
00299                                 [rsamples; errs(:)'], blocklength);
00300 %      C = ones(size(errs));
00301 %      i = logical(errs<this.stab_limit);
00302 %      C(i) = 2;
00303     end
00304 
00305     function print_3d_curve(this, datafile, fieldname)
00306       % function print_landscape(this, datafile, fieldname)
00307       % equivalent to the print_landscape() function put prints out a table
00308       % which can be interpreted for a 3d-curve plot e.g. by pgfplots/Tikz.
00309       %
00310       % This function can be used to display certain curves on the error
00311       % landscape generated by print_landscape().
00312       %
00313       % An example for a pgfplots graphics that can interpret the table output
00314       % is shown in the following code snippet: 
00315       % @code
00316       % \begin{axis}[zmode=log,log basis z=10,axis on top,
00317       %              view={-50}{-30}, axis lines=box,
00318       %              xlabel={$N$}, ylabel={$M$},
00319       %              zlabel={$\eta_{N,M,M'}$}]
00320       %  \addplot3[mark=0]
00321       %  table[x=N, y=M, z=error] {error_curve.dat};
00322       % @endcode
00323       %
00324       % Parameters:
00325       %  datafile:  a string specifying the path and filename of the data table to be stored
00326       %  fieldname: the fieldname in the #values attribute that shall be
00327       %             plotted @default 'max_err'
00328       if nargin <=2
00329         fieldname = 'max_err';
00330       end
00331       errs = this.reduce_slice(@max, fieldname);
00332       i = logical(errs>this.error_bound);
00333       errs(i) = this.error_bound;
00334       i = logical(isnan(errs));
00335       errs(i) = this.error_bound;
00336 
00337       Ns = ones(length(errs), 1);
00338       Ms = ones(length(errs), 1);
00339       rsamples = this.assessment.rsamples;
00340       for i=1:length(Ns)
00341         c = rsamples(i);
00342         this.assessment.update_rbmodel([], c);
00343         Ns(i) = this.assessment.rbmodel.N;
00344         Ms(i) = this.assessment.rbmodel.M;
00345       end
00346 
00347       print_datatable(datafile, {'c', 'N', 'M', 'error'}, ...
00348                                 [rsamples(:), Ns, Ms, errs(:)]');
00349     end
00350 
00351     function slic = get_slice(this, fieldname, cell_range, struct_range)
00352       % function slic = get_slice(this, fieldname, cell_range, out_range)
00353       % helper function slicing a field in the #values attribute
00354       %
00355       % Parameters:
00356       %  fieldname:    name of the field in the #values attributed to be sliced
00357       %  cell_range:   cell indices to be extracted from the field.
00358       %                If empty all cell entries are returned
00359       %                @default []
00360       %  struct_range: Usually the #values attribute is a cell of structure arrays.
00361       %                arrays. This range defines a slice in the structure
00362       %                array. If empty all array entries are returned.
00363       %                @default []
00364       %
00365       % Return values:
00366       %  slic:  the sliced #values attribute
00367       if nargin <= 2 || isempty(cell_range)
00368         cell_range = 1:length(this.values(:));
00369       end
00370       if nargin <= 3 || isempty(struct_range)
00371         struct_range = 1:length(this.values{1}(:));
00372       end
00373       if isequal(fieldname, 'rtime_est')
00374         slic = cellfun(@(x) x(struct_range), this.rtime_est(cell_range), 'UniformOutput', false);
00375       else
00376         slic = cellfun(@(x) [x(struct_range).(fieldname)], this.values(cell_range), 'UniformOutput', false);
00377       end
00378       slic = cell2mat(slic');
00379     end
00380 
00381     function rslice = reduce_slice(this, red_func, fieldname, cell_range, struct_range, direction)
00382       % function rslice = reduce_slice(this, red_func, fieldname, cell_range, struct_range, direction)
00383       % extends the get_slice() function by also applying a reduction function
00384       % on each matrix entry.
00385       %
00386       % Parameters:
00387       %  fieldname:    name of the field in the #values attributed to be sliced
00388       %  red_func:     function pointer who is applied to each matrix entry in
00389       %                the slice, e.g. '@@max' or '@@mean'
00390       %  cell_range:   cell indices to be extracted from the field. If empty
00391       %                all cell entries are returned.
00392       %                @default []
00393       %  struct_range: Usually the #values attribute is a cell of structure arrays.
00394       %                arrays. This range defines a slice in the structure
00395       %                array. If empty all array entries are returned.
00396       %                @default []
00397       %  direction:    integer specifying in which direction a matrix shall be
00398       %                reduced. In case it is equal to '2', the matrix is
00399       %                transposed before 'red_func' is applied.
00400       %                @default 1
00401       %
00402       % Return values:
00403       %  rslice:  the sliced and reduced #values attribute
00404       if nargin <=3
00405         cell_range = [];
00406       end
00407       if nargin <= 4
00408         struct_range = [];
00409       end
00410       if nargin <= 5
00411         direction = 1;
00412       end
00413       slic = get_slice(this, fieldname, cell_range, struct_range);
00414       if direction == 2
00415         slic   = slic';
00416         rslice = zeros(1,size(slic,1));
00417       else
00418         rslice = zeros(size(slic,1),1);
00419       end
00420       for i = 1:size(slic,1)
00421         rslice(i) = red_func(slic(i,:));
00422       end
00423     end
00424 
00425 
00426     function merge(this, other)
00427       % function merge(this, other)
00428       % helper function merging this Output object with another one.
00429       %
00430       % The following rules apply during merging:
00431       %  -# If 'values.max_Delta' is all 'Inf', copy 'max_Delta' and
00432       %     'max_Delta_ind' from 'other'.
00433       %  -# If 'values.max_err' is all 'Inf', copy 'max_err' and
00434       %     'max_err_ind' from 'other'.
00435       %  -# If one of 'rconds', 'dconds' or 'rtime_est' is empty, copy it
00436       %     from 'other'
00437       %  -# If one of 'rtime', 'rrtime' or 'dtime' is set to all-'NaN', copy it
00438       %     from 'other'
00439       %  -# If 'this' and 'other' have valid 'rtime' (i.e. not-'Nan') and only
00440       %     one of both has computed estimators, compute 'rtime_est' by
00441       %     subtracting one from the other.
00442       %
00443       % Parameters:
00444       %  other: another object of type Postprocess.StochasticAssessment.Output to be
00445       %         merged into this one.
00446       cpfields = {};
00447       rtime_exists = false;
00448       % check what we ar missing:
00449       if all([this.values{end}(:).max_Delta] == Inf)
00450         cpfields = [ cpfields, {'max_Delta', 'max_Delta_ind'}];
00451       end
00452       if all([this.values{end}(:).max_err] == Inf)
00453         cpfields = [ cpfields, {'max_err', 'max_err_ind'}];
00454       end
00455       if isempty([this.values{end}(:).rconds])
00456         cpfields = [ cpfields, {'rconds'}];
00457       end
00458       if isempty([this.values{end}(:).dconds])
00459         cpfields = [ cpfields, {'dconds'}];
00460       end
00461       if all(isnan([this.values{end}(:).rtime]))
00462         cpfields = [ cpfields, {'rtime'}];
00463       else
00464         rtime_exists = true;
00465       end
00466       if all(isnan([this.values{end}(:).rrtime]))
00467         cpfields = [ cpfields, {'rrtime'}];
00468       end
00469       if all(isnan([this.values{end}(:).dtime]))
00470         cpfields = [ cpfields, {'dtime'}];
00471       end
00472 
00473       for i = 1:length(cpfields)
00474         for j = 1:length(this.values(:))
00475           [this.values{j}(:).(cpfields{i})] = other.values{j}(:).(cpfields{i});
00476         end
00477       end
00478 
00479       if rtime_exists && any(~isnan([other.values{end}(:).rtime]))
00480         if this.assessment.compute_estimates ~= other.assessment.compute_estimates
00481           this.rtime_est = cell(size(this.values));
00482           for i = 1:length(this.values(:))
00483             this.rtime_est{i} = abs([this.values{i}(:).rtime] - [other.values{i}(:).rtime]);
00484           end
00485         end
00486       end
00487 
00488       if isempty(this.rtime_est)
00489         this.rtime_est = other.rtime_est;
00490       end
00491 
00492       if isempty(this.rd_conds)
00493         this.rd_conds = other.rd_conds;
00494       end
00495     end
00496   end
00497 end
 All Classes Namespaces Files Functions Variables