diff --git a/Utilities/ComputationalGraph/ComputationalGraphInteractiveTool.m b/Utilities/ComputationalGraph/ComputationalGraphInteractiveTool.m index 10e5fcf38..c9a1defd7 100644 --- a/Utilities/ComputationalGraph/ComputationalGraphInteractiveTool.m +++ b/Utilities/ComputationalGraph/ComputationalGraphInteractiveTool.m @@ -32,15 +32,23 @@ plotOptions + interactiveOptions + end methods function cgti = ComputationalGraphInteractiveTool(cg, varargin) - opt = struct('markStatic', true); + opt = struct('markStatic', true, ... + 'interactiveOptions', []); opt = merge_options(opt, varargin{:}); + interactiveOptions = setDefaultJsonStructField(opt.interactiveOptions, {'printStackSelectionAfterUpdate', 'position'}, 'before'); + interactiveOptions = setDefaultJsonStructField(interactiveOptions, 'plotAfterUpdate', true); + + cgti.interactiveOptions = interactiveOptions; + cgti.computationalGraph = cg; nodenames = cg.nodenames; @@ -935,6 +943,7 @@ function diff(cgti) varnames = varnameset{2}; + familyvarnames = {}; for ivar = 1 : numel(varnames) varname = varnames{ivar}; @@ -946,12 +955,12 @@ function diff(cgti) varnameinds = varnameinds(levels <= level); - varnames = union(varnames, cg.nodenames(varnameinds)); + familyvarnames = union(familyvarnames, cg.nodenames(varnameinds)); end - selection = {'set', varnames}; + selection = {'set', familyvarnames}; return case 'diff' @@ -977,6 +986,24 @@ function diff(cgti) function printStack(cgti) stack = cgti.stack; + + doplot = getJsonStructField(cgti.interactiveOptions, {'plotAfterUpdate'}, false); + + if doplot + cgti.plot(); + end + + position = getJsonStructField(cgti.interactiveOptions, {'printStackSelectionAfterUpdate', 'position'}); + + if isAssigned(position) && strcmp(position, 'before') + + cgti.printHeader('Selection') + cgti.printStackSelection(); + fprintf('\n'); + + end + + cgti.printHeader('Selector stack'); for iselector = numel(stack) : -1 : 1 lines = cgti.setupSelectorPrint(stack{iselector}); nlines = numel(lines); @@ -989,6 +1016,14 @@ function printStack(cgti) fprintf('%s%s\n', start, lines{iline}); end end + + if isAssigned(position) && strcmp(position, 'after') + + fprintf('\n'); + cgti.printHeader('Stack selection result after parsing') + cgti.printStackSelection(); + + end end @@ -1273,8 +1308,13 @@ function printFunctionDocs(functionDocs, parfill, oneline) end function printHeader(headertxt, n) - % Minor utility function used in this class to print a header with a number - str = sprintf('\n%d %s', n, headertxt); + % Minor utility function used in this class to print a header. The optional argument n can be used to include a + % number at the beginning of the header (often needed). + if nargin < 2 + str = sprintf('\n%s', headertxt); + else + str = sprintf('\n%d %s', n, headertxt); + end fprintf('%s:\n', str); fprintf('%s\n', repmat('-', length(str), 1)); end diff --git a/Utilities/Various/BatchProcessor.m b/Utilities/Various/BatchProcessor.m index cd767fb8f..1c567152c 100644 --- a/Utilities/Various/BatchProcessor.m +++ b/Utilities/Various/BatchProcessor.m @@ -1,4 +1,4 @@ -classdef BatchProcessor +classdef BatchProcessor < Selector %{ Copyright 2021-2024 SINTEF Industry, Sustainable Energy Technology @@ -21,12 +21,17 @@ %} properties + paramnames + end methods function bp = BatchProcessor(varargin) + + bp = bp@Selector(); + bp.paramnames = {}; if nargin > 0 simlist = varargin{1}; @@ -246,7 +251,8 @@ function assertParam(bp, paramname) assert(ismember(paramname, bp.paramnames), sprintf('parameter %s not registered', paramname)); end - function printSimList(bp, simlist, varargin) + function print(bp, simlist, varargin) + [T, singlevalued] = bp.setupTable(simlist); if nargin > 2 && strcmp(varargin{1}, 'all') paramnames = bp.paramnames; @@ -283,7 +289,7 @@ function printSimList(bp, simlist, varargin) if numel(paramnames) == 0 if numel(simlist) > 1 % we print all the parameters instead of empty table - printSimList(bp, simlist, 'all'); + bp.print(simlist, 'all'); else simlist{1} end @@ -292,7 +298,7 @@ function printSimList(bp, simlist, varargin) end end - function sortedsimlist = sortSimList(bp, simlist, varargin) + function sortedsimlist = sort(bp, simlist, varargin) paramname = varargin{end}; rest = varargin(1 : end - 1); @@ -351,10 +357,10 @@ function printSimList(bp, simlist, varargin) [bp, simlist] = bp.mergeSimLists(simlist, simlist_to_merge); end - - - function filteredsimlist = filterSimList(bp, simlist, varargin) + + function filteredsimlist = filter(bp, simlist, varargin) assert(mod(numel(varargin), 2) == 0, 'wrong number of argument') + filteredsimlist = {}; paramname = varargin{1}; filter = varargin{2}; @@ -394,8 +400,102 @@ function printSimList(bp, simlist, varargin) if ~isempty(rest) filteredsimlist = bp.filterSimList(filteredsimlist, rest{:}); end - end - + end + + + %%%%%%%%%%%% + %% Selector class overloaded function + %%%%%%%%%%%%%% + + function str = selectSelectorToString(slt, selectSelector) + % Returns the printed form of a 'select' selector + + assert(strcmp(selectSelector{1}, 'select'), 'this is not a select type selector'); + selector_type = selectSelector{2}{1}; + selector_value = selectSelector{2}{2}; + + if isa(selector_value, 'function_handle') + selector_value = func2str(selector_value); + end + + str = sprintf('%s : %s', selector_type, selector_value); + + end + + function printSelection(slt, givenset, selection) + + if ~strcmp(selection{1}, 'set') + selection = slt.parseSelector(givenset, selection); + end + + inds = selection{2}; + + simlist = givenset(inds); + + printall = getStructField(slt.interactiveOptions, {'printSelection', 'all'}, true); + + if printall + slt.print(simlist, 'all'); + else + slt.print(simlist, 'all'); + end + + end + + function found = find(slt, givenset, selector) + + paramname = selector{1}; + filter = selector{2}; + + found = []; + + for ielt = 1 : numel(givenset) + + elt = givenset{ielt}; + + paramnames = fieldnames(elt); + + indparams = regexpSelect(paramnames, paramname); + + for iindparams = 1 : numel(indparams) + + indparam = indparams(iindparams); + + paramval = elt.(paramnames{indparam}); + + take = false; + + if (isempty(filter) | strcmp(filter, 'undefined')) + if isempty(paramval) + take = true; + end + elseif isa(filter, 'numeric') || isa(filter, 'logical') + if paramval == filter + take = true; + end + elseif isa(filter, 'char') + if strcmp(paramval, filter) + take = true; + end + elseif isa(filter, 'function_handle') + if filter(paramval) + take = true; + end + else + error('filter type not recognized'); + end + if take == true + found(end + 1) = ielt; + end + end + + end + + found = unique(found); + + end + + end methods(Static) diff --git a/Utilities/Various/Selector.m b/Utilities/Various/Selector.m new file mode 100644 index 000000000..dc4f270cb --- /dev/null +++ b/Utilities/Various/Selector.m @@ -0,0 +1,390 @@ +classdef Selector < handle + + properties + + stack = {} % stack of selectors (plotting tool) + + interactiveOptions + + end + + methods + + function slt = Selector(varargin) + + opt = struct('interactiveOptions', []); + opt = merge_options(opt, varargin{:}); + + interactiveOptions = setDefaultStructField(opt.interactiveOptions, 'printStackAfterUpdate', true); + + slt.interactiveOptions = interactiveOptions; + + end + + function booleanOperator(slt, op, n) + + assert(ismember(op, {'and', 'or'}), 'boolean operator not recognized'); + + stack = slt.stack; + + if nargin < 3 + n = 2; + end + + if numel(stack) < n + error(sprintf('%s operation require %d elements in this call'), op, n); + end + + slt.stack = { {op, stack(1 : n)}, stack{n + 1 : end}}; + + if slt.interactiveOptions.printStackAfterUpdate + slt.printStack(); + end + + end + + function and(slt, n) + + if nargin < 2 + n = 2; + end + + slt.booleanOperator('and', n); + + end + + function or(slt, n) + + if nargin < 2 + n = 2; + end + + slt.booleanOperator('or', n); + + end + + function select(slt, expr) + + slt.stack = {{'select', expr}, slt.stack{:}}; + if slt.interactiveOptions.printStackAfterUpdate + slt.printStack(); + end + + end + + function reset(slt) + + slt.stack = {}; + + end + + function del(slt, n) + + if nargin < 2 + n = 1; + end + + stack = slt.stack; + + assert(numel(stack) >= n, sprintf('I cannot remove %d elements in the stack. Stack contains %d elements', n, numel(stack))); + + slt.stack = stack(n + 1 : end); + + if slt.interactiveOptions.printStackAfterUpdate + slt.printStack(); + end + + end + + function delop(slt) + + stack = slt.stack; + + assert(numel(stack) >= 1, 'stack is empty'); + + selector = stack{1}; + + stack = stack(2 : end); + + selectortype = selector{1}; + + switch selectortype + + case {'select', 'set'} + + error('no operator at bottom of the stack'); + + case {'and', 'or', 'diff'} + + slt.stack = {selector{2}{:}, stack{:}}; + + slt.stack = {selector{3}, stack{:}}; + + end + + if slt.interactiveOptions.printStackAfterUpdate + slt.printStack(); + end + + end + + function dup(slt) + + stack = slt.stack; + + assert(numel(stack) > 0, 'stack is empty'); + + slt.stack = {stack{1}, stack{1}, stack{2 : end}}; + + if slt.interactiveOptions.printStackAfterUpdate + slt.printStack(); + end + + end + + + function swap(slt, n) + + stack = slt.stack; + + if nargin < 2 + n = 2; + end + + assert(numel(stack) >= n, 'There should be at least %d elements in the stack to swap the %dth element', n); + + inds = (1 : numel(stack)); + inds(n) = []; + inds = [n, inds]; + + slt.stack = stack(inds); + + if slt.interactiveOptions.printStackAfterUpdate + slt.printStack(); + end + + end + + function diff(slt) + + stack = slt.stack; + + assert(numel(stack) >= 2, 'There should be at least 2 elements in the stack to take a diff'); + + slt.stack = {{'diff', {stack{1}, stack{2}}}, stack{3 : end}}; + + if slt.interactiveOptions.printStackAfterUpdate + slt.printStack(); + end + + + end + + + function found = find(slt, givenset, criteria) + % Returns the indices that are matched in the given set by the criteria + + error('base function') + + end + + function str = elementToString(slt, element) + % Returns the printed form of the element + + error('base function') + + end + + function selection = parseSelector(slt, givenset, selector) + + selectiontype = selector{1}; + + assert(ischar(selectiontype), 'The first element of the selector should be a string'); + + switch selectiontype + + case 'set' + + return + + case 'select' + + inds = slt.find(givenset, selector{2}); + + selection = {'set', inds}; + + return + + case {'and', 'or'} + + % list of the arguments for the boolean operator, which consist of a list of selector + boolean_arg_selectors = selector{2}; + + % We parse the first selector + boolean_arg_set = slt.parseSelector(givenset, boolean_arg_selectors{1}); + + % We recover the indices + boolean_arg_ind = boolean_arg_set{2}; + + for iselect = 1 : numel(boolean_arg_selectors) + boolean_arg_set = slt.parseSelector(givenset, boolean_arg_selectors{iselect}); + switch selectiontype + case 'and' + boolean_arg_ind = intersect(boolean_arg_ind, boolean_arg_set{2}); + case 'or' + boolean_arg_ind = union(boolean_arg_ind, boolean_arg_set{2}); + end + end + + selection = {'set', boolean_arg_ind}; + + return + + case 'diff' + + error('not updated yet'); + + args = selector{2}; + + assert(numel(args) == 2, 'we expect 2 arguments for a diff'); + + for iarg = 1 : numel(args) + varnameset = slt.parseSelector(args{iarg}); + varnames{iarg} = varnameset{2}; + end + + varnames = setdiff(varnames{2}, varnames{1}); + selection = {'set', varnames}; + + return + + end + + end + + function printStack(slt) + + stack = slt.stack; + for iselector = numel(stack) : -1 : 1 + lines = slt.setupSelectorPrint(stack{iselector}); + nlines = numel(lines); + for iline = nlines : -1 : 1 + if iline == 1 + start = sprintf('%2d: ', iselector); + else + start = ' '; + end + fprintf('%s%s\n', start, lines{iline}); + end + end + + end + + function inds = parseStack(slt, givenset) + + inds = slt.parseSelector(givenset, slt.stack{1}); + + end + + function subset = extract(slt, givenset) + + inds = slt.parseStack(givenset); + subset = givenset(inds{2}); + + end + + + function printStackSelection(slt, givenset) + + assert(numel(slt.stack) > 0, 'stack is empty'); + + slt.printSelection(givenset, slt.stack{1}); + + end + + function printsel(slt, givenset) + + % shortcut + slt.printStackSelection(givenset); + + end + + function printall(slt, givenset) + + selection = {'set', (1 : numel(givenset))}; + slt.printSelection(givenset, selection); + + end + + function printSelection(slt, givenset, selection) + + if ~strcmp(selection{1}, 'set') + selection = slt.parseSelector(givenset, selection); + end + + inds = selection{2}; + fprintf('\n'); + + for ivar = 1 : numel(inds) + ind = inds(ivar) + fprintf('%s\n', slt.elementToString(givenset{ind})); + end + + end + + function str = selectSelectorToString(slt, selectSelector) + % Returns the printed form of a 'select' selector + + error('base function'); + + end + function lines = setupSelectorPrint(slt, selector) + + indent0 = ' '; + + function lines = setupLines(selector, indent) + + selectortype = selector{1}; + + switch selectortype + + case 'select' + + lines{1} = sprintf('%s%s ''%s''', indent, selectortype, slt.selectSelectorToString(selector)); + return + + case {'and', 'or', 'diff'} + + lines{1} = sprintf('%s%s', indent, selectortype); + subselectors = selector{2}; + for isel = 1 : numel(subselectors) + lines = horzcat(lines, setupLines(subselectors{isel}, [indent, indent0])); + end + return + + end + + + end + + lines = setupLines(selector, ''); + + end + + function printSelector(slt, selector) + + lines = slt.setupSelectorPrint(selector); + + for iline = numel(lines) : -1 : 1 + fprintf('%s\n', lines{iline}); + end + + end + + + + end + +end + + diff --git a/Utilities/ComputationalGraph/regexpSelect.m b/Utilities/Various/regexpSelect.m similarity index 94% rename from Utilities/ComputationalGraph/regexpSelect.m rename to Utilities/Various/regexpSelect.m index 697071ee2..d6365001e 100644 --- a/Utilities/ComputationalGraph/regexpSelect.m +++ b/Utilities/Various/regexpSelect.m @@ -9,6 +9,8 @@ end inds = unique(inds); else + name = replace(name, '{', '\{'); + name = replace(name, '}', '\}'); name = regexprep(name, ' +', '.*'); inds = regexp(names, name, 'once'); inds = cellfun(@(x) ~isempty(x), inds);