From c21cd3f37aa2ca3a9127939f036317bb3b6f3cc6 Mon Sep 17 00:00:00 2001
From: Michael Innerberger <michael.innerberger@asc.tuwien.ac.at>
Date: Wed, 9 Aug 2023 16:38:45 -0400
Subject: [PATCH] Clean up LevelDataCollection

---
 .../LevelDataCollection.m                     | 71 ++++++++-----------
 lib/storage/@LevelDataCollection/get.m        | 31 ++++----
 .../@LevelDataCollection/plotStatistics.m     | 27 ++++---
 .../@LevelDataCollection/printHeader.m        |  5 +-
 lib/storage/@LevelDataCollection/printItem.m  | 12 ++--
 .../@LevelDataCollection/printStatistics.m    | 28 ++++----
 lib/storage/@LevelDataCollection/printTable.m |  6 +-
 lib/storage/@LevelDataCollection/saveToFile.m | 11 ++-
 .../@LevelDataCollection/saveToTable.m        | 11 ++-
 lib/storage/@LevelDataCollection/set.m        | 18 +++--
 lib/storage/TimeIt.m                          | 21 +++---
 11 files changed, 122 insertions(+), 119 deletions(-)

diff --git a/lib/storage/@LevelDataCollection/LevelDataCollection.m b/lib/storage/@LevelDataCollection/LevelDataCollection.m
index 623d50f..6cc2e4f 100644
--- a/lib/storage/@LevelDataCollection/LevelDataCollection.m
+++ b/lib/storage/@LevelDataCollection/LevelDataCollection.m
@@ -41,13 +41,6 @@ classdef LevelDataCollection < handle
         isInitialRun (1,1) boolean
     end
 
-    properties (Dependent, Access=private)
-        % String specifying output format in printItem
-        formatSpecifier (1,:) char
-        % String specifying header format in prinItem
-        headerSpecifier (1,:) char
-    end
-
     properties (Access=protected)
         % Separator for printing to command line
         separator
@@ -66,8 +59,6 @@ classdef LevelDataCollection < handle
             end
             ensureFolderExists(obj.root);
 
-            % TODO: set output of hostname to string and remove cast
-            % TODO: does it make sense to store this here? -> see e.g. get.problem before
             obj.metaData = dictionary(...
                 "problem", "problem", ...
                 "domain", "domain", ...
@@ -115,37 +106,26 @@ classdef LevelDataCollection < handle
             file = obj.metaData("identifier") + '_collection';
         end
 
-        function spec = get.headerSpecifier(obj)
-            % Creates formatting string for the header of the output 
-            % to command line
-            spec = [' run', obj.separator];
+        function spec = getHeaderSpecifier(obj, separator)
+            % Creates formatting string for the header of the output to command line
+            spec = cell(1, obj.nTimeVariable+1);
+            spec{1} = ' run';
             for j = 1:obj.nTimeVariable
                 t = obj.item{1}.type(obj.timeVariable{j});
-                if j < obj.nTimeVariable
-                    spec = [spec, '%', obj.getWidth(t), 's', obj.separator]; %#ok<*AGROW>
-                else
-                    spec = [spec, '%', obj.getWidth(t), 's\n'];
-                end
-            end
-            if obj.nTimeVariable == 0
-                spec = [spec, '\n'];
+                spec{j+1} = assembleSpecifier(obj.getWidth(t), 's');
             end
+            spec = strjoin(spec, separator) + "\n";
         end
 
-        function spec = get.formatSpecifier(obj)
+        function spec = getFormatSpecifier(obj, separator)
             % Creates formatting string for printing to command line
-            spec = ['%4d', obj.separator];
+            spec = cell(1, obj.nTimeVariable+1);
+            spec{1} = '%4d';
             for j = 1:obj.nTimeVariable
                 t = obj.item{1}.type(obj.timeVariable{j});
-                if j < obj.nTimeVariable
-                    spec = [spec, '%', obj.getWidth(t), t.formatSpec, obj.separator];
-                else
-                    spec = [spec, '%', obj.getWidth(t), t.formatSpec, '\n'];
-                end
-            end
-            if obj.nTimeVariable == 0
-                spec = [spec, '\n'];
+                spec{j+1} = assembleSpecifier(obj.getWidth(t), t.formatSpec);
             end
+            spec = strjoin(spec, separator) + "\n";
         end
 
         %% READ ITEM DATA
@@ -154,14 +134,21 @@ classdef LevelDataCollection < handle
         %% MODIFY ITEMS
         set(obj, jItem, varargin)
 
-        function append(obj, varargin)
-            %%APPEND simplified addition specified data to this list, the
-            %cell array varargin must contain LevelData objects
-            %   APPEND(obj, varargin)
-            assert(all(isa(varargin{:}, 'LevelData')), ...
-                   'Arguments must be of class LevelData');
-            indices = obj.nItem + (1:length(varargin));
-            obj.set(indices, varargin{:});
+        function append(obj, data)
+            %%APPEND simplified addition specified of data to this list, one or
+            %more LevelData objects
+            %   APPEND(obj, data)
+
+            arguments
+                obj
+            end
+
+            arguments (Repeating)
+                data LevelData
+            end
+
+            indices = obj.nItem + (1:length(data));
+            obj.set(indices, data{:});
         end
 
         function remove(obj, indices)
@@ -187,6 +174,10 @@ classdef LevelDataCollection < handle
             width = num2str(max(type.printWidth, obj.minimalWidth));
         end
 
-        printTable(obj, fid, variableName, data)
+        printTable(obj, fid, variableName, data, separator)
     end
 end
+
+function spec = assembleSpecifier(width, format)
+    spec = ['%', num2str(width), format];
+end
\ No newline at end of file
diff --git a/lib/storage/@LevelDataCollection/get.m b/lib/storage/@LevelDataCollection/get.m
index 57e2446..025502e 100644
--- a/lib/storage/@LevelDataCollection/get.m
+++ b/lib/storage/@LevelDataCollection/get.m
@@ -1,9 +1,9 @@
-function output = get(obj, jItem, varargin)
+function output = get(obj, jItem, variableName)
 %%GET extracts data from this LevelDataCollection object for a given list
-%jItem of item numbers, the cell array varargin must contain the names of 
-%the variables to return
-%   output = GET(obj, jItem, varargin)
-%   output = GET(obj, ':', varargin)
+%jItem of item numbers. One or more variables to be returned can be specified by
+%their names.
+%   output = GET(obj, jItem, variableName, ...)
+%   output = GET(obj, ':', variableName, ...)
 
 % Copyright 2023 Philipp Bringmann
 %
@@ -21,30 +21,37 @@ function output = get(obj, jItem, varargin)
 % along with this program.  If not, see <http://www.gnu.org/licenses/>.
 %
 
+    arguments
+        obj
+        jItem {mustBeIndexVector} = ':'
+    end
+
+    arguments (Repeating)
+        variableName {mustBeTextScalar}
+    end
 
-    % Proceed input
     if strcmp(jItem, ':')
         jItem = 1:obj.nItem;
     end
 
     % Initialise output variable
-    output = cell(1, length(varargin));
+    output = cell(1, length(variableName));
 
     % Determine number of levels (uses first item!)
     nLevel = obj.item{1}.nLevel;
 
     % Iterate over variables
-    for jVariable = 1:length(varargin)
-        variableName = varargin{jVariable};
+    for jVariable = 1:length(variableName)
+        name = variableName{jVariable};
         % Check for presence of variable
-        if ~ismember(variableName, obj.timeVariable)
-            warning(['Variable ', variableName, ' not found']);
+        if ~ismember(name, obj.timeVariable)
+            warning(['Variable ', name, ' not found']);
             data = [];
         else
             data = zeros(nLevel, length(jItem));
             % Iterate over items
             for k = 1:length(jItem)
-                data(:,k) = obj.item{jItem(k)}.get(':', variableName);
+                data(:,k) = obj.item{jItem(k)}.get(':', name);
             end
         end
         % Store to return variable
diff --git a/lib/storage/@LevelDataCollection/plotStatistics.m b/lib/storage/@LevelDataCollection/plotStatistics.m
index 20ce372..3c81e35 100644
--- a/lib/storage/@LevelDataCollection/plotStatistics.m
+++ b/lib/storage/@LevelDataCollection/plotStatistics.m
@@ -1,8 +1,8 @@
-function ax = plotStatistics(obj, xVariable, varargin)
+function ax = plotStatistics(obj, xVariable, yVariable)
 %%PLOTSTATISTICS plots with statistical information (mean value with min
-%and max value) of scalar time variables (specified in varargin) with
+%and max value) of scalar time variables (specified in yVariable) with
 %respect to the variable xVariable, returns handle to axis object
-%   PLOTSTATISTICS(obj, xVariable, varargin)
+%   PLOTSTATISTICS(obj, xVariable, yVariable, ...)
 
 % Copyright 2023 Philipp Bringmann
 %
@@ -20,25 +20,30 @@ function ax = plotStatistics(obj, xVariable, varargin)
 % along with this program.  If not, see <http://www.gnu.org/licenses/>.
 %
 
+    arguments
+        obj
+        xVariable {mustBeTextScalar}
+    end
+
+    arguments (Repeating)
+        yVariable
+    end
 
     % Choose time variables for plotting
-    if nargin < 3
-        varargin = obj.timeVariable;
+    if isempty(yVariable)
+        yVariable = obj.timeVariable;
     else
-        varargin = intersect(varargin, obj.timeVariable);
+        yVariable = intersect(yVariable, obj.timeVariable);
     end
 
-    % Specify separator for table
-    obj.separator = '  ';
-
     % Extract data for x-axis
     xData = obj.item{1}.get(':', xVariable);
 
     % Extract data from each item and on each level
-    data = obj.get(':', varargin{:});
+    data = obj.get(':', yVariable{:});
 
     % Create plot
-    ax = plotData(xData, varargin, data);
+    ax = plotData(xData, yVariable, data);
 
     % Add title
     title(ax, 'Runtime plot');
diff --git a/lib/storage/@LevelDataCollection/printHeader.m b/lib/storage/@LevelDataCollection/printHeader.m
index 4dadef4..b3225a5 100644
--- a/lib/storage/@LevelDataCollection/printHeader.m
+++ b/lib/storage/@LevelDataCollection/printHeader.m
@@ -21,11 +21,8 @@ function printHeader(obj)
 %
 
 
-    % Set separator variable
-    obj.separator = '  ';
-
     % Create header with variable names
-    header = sprintf(obj.headerSpecifier, obj.timeVariable{:});
+    header = sprintf(obj.getHeaderSpecifier('  '), obj.timeVariable{:});
 
     % Print header to command line
     fprintf(header);
diff --git a/lib/storage/@LevelDataCollection/printItem.m b/lib/storage/@LevelDataCollection/printItem.m
index 8238c19..b83bfd2 100644
--- a/lib/storage/@LevelDataCollection/printItem.m
+++ b/lib/storage/@LevelDataCollection/printItem.m
@@ -22,10 +22,9 @@ function printItem(obj, jItem)
 % along with this program.  If not, see <http://www.gnu.org/licenses/>.
 %
 
-
-    % Print final item by default
-    if nargin < 2
-        jItem = obj.nItem;
+    arguments
+        obj
+        jItem = obj.nItem
     end
 
     % Print header in case of plotting the first level
@@ -33,9 +32,6 @@ function printItem(obj, jItem)
         obj.printHeader();
     end
 
-    % Set separator variable
-    obj.separator = '  ';
-
     % Iterate over given list of item indices
     for k = 1:length(jItem)
         % Extract data of all time variables
@@ -47,6 +43,6 @@ function printItem(obj, jItem)
             data{ind} = obj.item{jItem(k)}.level.(obj.timeVariable{l});
         end
         % Print information on current item to command line
-        fprintf(obj.formatSpecifier, data{1:ind});
+        fprintf(obj.getFormatSpecifier('  '), data{1:ind});
     end
 end
\ No newline at end of file
diff --git a/lib/storage/@LevelDataCollection/printStatistics.m b/lib/storage/@LevelDataCollection/printStatistics.m
index 6bc8cad..1c9766a 100644
--- a/lib/storage/@LevelDataCollection/printStatistics.m
+++ b/lib/storage/@LevelDataCollection/printStatistics.m
@@ -1,7 +1,7 @@
-function printStatistics(obj, varargin)
+function printStatistics(obj, variable)
 %%PRINTSTATISTICS prints statististical information of scalar time
-%variables (specified in varargin)
-%   PRINTSTATISTICS(obj, varargin)
+%variables (specified in variable)
+%   PRINTSTATISTICS(obj, variable, ...)
 
 % Copyright 2023 Philipp Bringmann
 %
@@ -19,26 +19,30 @@ function printStatistics(obj, varargin)
 % along with this program.  If not, see <http://www.gnu.org/licenses/>.
 %
 
+    arguments
+        obj
+    end
+
+    arguments (Repeating)
+        variable {mustBeTextScalar}
+    end
 
     % Choose time variables for plotting
-    if nargin < 2
-        varargin = obj.timeVariable;
+    if isempty(variable)
+        variable = obj.timeVariable;
     else
-        varargin = intersect(varargin, obj.timeVariable);
+        variable = intersect(variable, obj.timeVariable);
     end
 
-    % Specify separator for table
-    obj.separator = '  ';
-
     % Extract data from each item and on each level
-    data = obj.get(':', varargin{:});
+    data = obj.get(':', variable{:});
 
     % Print separator between tables
     fprintf('\n');
 
     % Print table for each given variable
-    for j = 1:length(varargin)
-        obj.printTable(1, varargin{j}, data{j});
+    for j = 1:length(variable)
+        obj.printTable(1, variable{j}, data{j}, '  ');
         fprintf('\n\n');
     end
 end
diff --git a/lib/storage/@LevelDataCollection/printTable.m b/lib/storage/@LevelDataCollection/printTable.m
index 647fb2c..7756663 100644
--- a/lib/storage/@LevelDataCollection/printTable.m
+++ b/lib/storage/@LevelDataCollection/printTable.m
@@ -1,4 +1,4 @@
-function printTable(obj, fid, variableName, data)
+function printTable(obj, fid, variableName, data, separator)
 %%PRINTTABLE auxiliary private function for printing statististical
 %information for data of scalar time variables variableName to command
 %line (fid=1) or file (specified by file identifier fid)
@@ -23,7 +23,7 @@ function printTable(obj, fid, variableName, data)
 
     % Define formatting strings for title and headline
     TITLE = ['# TIME STATISTICS - ', variableName];
-    HEADLINE = sprintf(['%5s', repmat([obj.separator, '%11s'], 1, 5)],...
+    HEADLINE = sprintf(['%5s', repmat([separator, '%11s'], 1, 5)],...
                         'level', 'mean', 'median', 'std', 'min', 'max');
 
     if fid == 1
@@ -43,6 +43,6 @@ function printTable(obj, fid, variableName, data)
                        max(data, [], 2)];
 
     % Print information
-    fprintf(fid, ['%5d', repmat([obj.separator, '%8.5e'], 1, 5), '\n'],...
+    fprintf(fid, ['%5d', repmat([separator, '%8.5e'], 1, 5), '\n'],...
             [1:size(data, 1); permute(statisticalData, [2 1])]);
 end
\ No newline at end of file
diff --git a/lib/storage/@LevelDataCollection/saveToFile.m b/lib/storage/@LevelDataCollection/saveToFile.m
index f8236c2..d1aca0c 100644
--- a/lib/storage/@LevelDataCollection/saveToFile.m
+++ b/lib/storage/@LevelDataCollection/saveToFile.m
@@ -21,13 +21,10 @@ function saveToFile(obj, folder, file)
 % along with this program.  If not, see <http://www.gnu.org/licenses/>.
 %
 
-
-    % Proceed optional input
-    if nargin < 2
-        folder = obj.foldername;
-    end
-    if nargin < 3
-        file = obj.filename;
+    arguments
+        obj
+        folder = obj.foldername
+        file = obj.filename
     end
 
     % Create problem- and method-specific folder
diff --git a/lib/storage/@LevelDataCollection/saveToTable.m b/lib/storage/@LevelDataCollection/saveToTable.m
index 6f748df..1ec56e3 100644
--- a/lib/storage/@LevelDataCollection/saveToTable.m
+++ b/lib/storage/@LevelDataCollection/saveToTable.m
@@ -21,12 +21,9 @@ function saveToTable(obj, separator)
 % along with this program.  If not, see <http://www.gnu.org/licenses/>.
 %
 
-
-    % Proceed optional input
-    if nargin < 2
-        obj.separator = ',';
-    else
-        obj.separator = separator;
+    arguments
+        obj
+        separator {mustBeTextScalar} = ','
     end
 
     % Create problem- and method-specific folder
@@ -37,7 +34,7 @@ function saveToTable(obj, separator)
     for j = 1:obj.nTimeVariable
         fid = fopen(obj.foldername + '/' + obj.filename + '_' + ...
                      obj.timeVariable{j} + '.csv', 'w');
-        obj.printTable(fid, obj.timeVariable{j}, data{j});
+        obj.printTable(fid, obj.timeVariable{j}, data{j}, separator);
         fclose(fid);
     end
 end
diff --git a/lib/storage/@LevelDataCollection/set.m b/lib/storage/@LevelDataCollection/set.m
index 70b20fa..6ca051f 100644
--- a/lib/storage/@LevelDataCollection/set.m
+++ b/lib/storage/@LevelDataCollection/set.m
@@ -1,7 +1,7 @@
-function set(obj, jItem, varargin)
+function set(obj, jItem, data)
 %%SET stores specified list of LevelData objects to the indices determined
 %in the jItem array
-%   SET(obj, jItem, varargin)
+%   SET(obj, jItem, data)
 
 % Copyright 2023 Philipp Bringmann
 %
@@ -19,17 +19,21 @@ function set(obj, jItem, varargin)
 % along with this program.  If not, see <http://www.gnu.org/licenses/>.
 %
 
+    arguments
+        obj
+        jItem
+    end
 
-    % Check type of input
-    assert(all(isa(varargin{:}, 'LevelData')), ...
-           'Arguments must be of class LevelData');
+    arguments (Repeating)
+        data LevelData
+    end
 
     % Number of items and item indices must coincide
-    assert(length(jItem) == length(varargin), ...
+    assert(length(jItem) == length(data), ...
            'Number of indices must equal number of given arguments');
 
     % Store items
     for k = 1:length(jItem)
-        obj.item{jItem(k)} = varargin{k};
+        obj.item{jItem(k)} = data{k};
     end
 end
\ No newline at end of file
diff --git a/lib/storage/TimeIt.m b/lib/storage/TimeIt.m
index 60df811..b437843 100644
--- a/lib/storage/TimeIt.m
+++ b/lib/storage/TimeIt.m
@@ -1,7 +1,7 @@
-function leveldatacollection = TimeIt(identifier, nRun, functionName, varargin)
+function leveldatacollection = TimeIt(identifier, nRun, functionName, arguments)
 %%TIMEIT function wrapper for multiple runs of functions storing timing
 %data in LevelData objects
-%   leveldatacollection = TIMEIT(identifier, nRun, functionName, varargin)
+%   leveldatacollection = TIMEIT(identifier, nRun, functionName, arguments, ...)
 
 % Copyright 2023 Philipp Bringmann
 %
@@ -19,9 +19,14 @@ function leveldatacollection = TimeIt(identifier, nRun, functionName, varargin)
 % along with this program.  If not, see <http://www.gnu.org/licenses/>.
 %
 
-    % Proceed input
-    if nargin < 4
-        varargin = cell(0);
+    arguments
+        identifier
+        nRun
+        functionName
+    end
+
+    arguments (Repeating)
+        arguments
     end
 
     % Welcome statement
@@ -37,7 +42,7 @@ function leveldatacollection = TimeIt(identifier, nRun, functionName, varargin)
     for j = 1:nRun
         % Run current experiment
         temporaryIdentifier = sprintf('timingRun%04d', j);
-        outputList = fevalc(functionName, varargin{:});
+        outputList = fevalc(functionName, arguments);
         leveldata = outputList{1};
         leveldata.metaData("identifier") = temporaryIdentifier;
         % Remove unused information
@@ -55,11 +60,11 @@ function leveldatacollection = TimeIt(identifier, nRun, functionName, varargin)
 end
 
 
-function output = fevalc(functionName, varargin) %#ok<INUSD>
+function output = fevalc(functionName, arguments) %#ok<INUSD>
 %%FEVALC suppresses output to commandline
 
     % Create function call
-    functioncall = 'feval(functionName, varargin{:})';
+    functioncall = 'feval(functionName, arguments{:})';
     % Call function
 	[~, output] = evalc(functioncall);
     % Store output in cell variable
-- 
GitLab