classdef GUItrain < handle
    % Graphical user interface to train and validate local model networks
    %
    % Graphical user interface. You can train hilomot and lolimot objects, 
    % view the train data error, the test data error as well as the AIC 
    % criterion. Furthermore you can perform a cross validation.
    %
    %
    % PROPERTIES:
    %
    % figureHandle:             Handle of the figure containing the GUI
    % trainData:        (? x 1) Cell-array containing all dataSet objects
    %                           the user read in to train a LMN
    % testData:         (? x 1) Cell-array containing all dataSet objects
    %                           the user read in to test a LMN
    % model:            (? x 1) Cell-array containing all LMN objects the
    %                           user read in or trained within the GUI
    % TDelements:       (1 x 1) The current number of dataSet objects to
    %                           train LMNs
    % LMNelements:      (1 x 1) The current number of LMN objects the user
    %                           read in or trained within the GUI
    % testDataElements: (1 x 1) The current number of dataSet objects to
    %                           test LMNs
    %
    %
    % HIDDEN PROPERTIES
    %
    % crossValidationOptions:   Structure with fields 'localModels' and
    %                           'numberOfGroups'. The field 'localModels'
    %                           contains a (1 x ?) vector. Every entry
    %                           within this vector represents a model
    %                           complexity for which the cross validation
    %                           should be evaluated. The field.
    %                           'numberOfGroups' is (1 x 1) integer, that
    %                           determines in how many groups the whole
    %                           training data should be splitted for the
    %                           cross validation. The maximum value
    %                           therefor is the number of training samples,
    %                           which leads to a leaf-one-out cross
    %                           validation.
    % crossValidationValues:    (1 x nc) Vector, that contains the cross
    %                           validation error for each of the nc model
    %                           complexities.
    % showCVwarning:              Logical to determine if a warning should be
    %                           shown as soon as the user is about to
    %                           perform a cross validation.
    %
    %
    % SYMBOLS AND ABBREVIATIONS:
    %
    % LMN:  Local model network
    % GUI:  Graphical user interface
    %
    % p:   Number of inputs (physical inputs)
    % q:   Number of outputs
    % N:   Number of data samples
    % M:   Number of LMs
    % nx:  Number of regressors (x)
    % nz:  Number of regressors (z)
    % nc:  Maximum number of local models
    
    % Julian Belz, 21-November-2011
    % Institut für Mess- und Regelungstechnik, Universität Siegen, Deutschland
    % Institute of Measurement and Control, University of Siegen, Germany
    % Copyright (c) 2011 by Julian Belz
    
    % 15. March 2012: Name of the class changed, integration of additional features (JB)
    
    properties (SetAccess = private)
        figureHandle % The handle of the figure that contains the GUI
        trainData % Cell-array containing all dataSet objects for LMN training
        testData % Cell-array containing all dataSet objects for LMN testing
        model % Cell-array containing all LMN objects (first colum) and crossvalidation options (second colum)
        TDelements  = 0; % Number of all dataSet objects for LMN training
        LMNelements = 0; % Number of all LMN objects
        testDataElements = 0; % Number of all dataSet objects for LMN testing
    end
    
    properties(Hidden=true)
        showCVwarning = 1;           % Flag to distinguish whether the cross-validation (CV) warning should be shown or not
        showTrainingSuccess = true;  % Flag to distinguish whether information about the training iterations should be displayed in the command window
    end
    
    methods
        function fh = GUItrain(modelObject)
            % Initializes the GUI
            %
            % fh = GUItrain(modelObject,modelDescription)
            %
            % INPUTS
            %
            % modelObject (optional):       Model object. For example a
            %                               hilomot object.
            % modelDescription (optional):  String that describes the model
            %                               object.
            
            % INPUTS are used after the figure is initialized!
            
            % View options
            linewidth  = 2;
            fontsize   = 13;
            fontname   = 'Times New Roman';
            leftcolor  = [0 0 0];
            rightcolor = [0 0 1];
            pcolor     = [leftcolor;rightcolor];
            leftline   = '-';
            rightline  = ':';
            pline      = {leftline,rightline};
            width      = 1000;
            height     = 650;
            scrsize    = get(0,'screensize');
            fhPos      = [(scrsize(3) - width)/2 ...
                (scrsize(4) - height)/2 ...
                width ...
                height];
            FSueber   = 15; % Fontsize for headlines
            FNueber   = 'Times New Roman'; % fontname for headlines
            bgcolor   = [0.7 0.7 0.7];
            white     = [  1   1   1];
            kritList  = {'None','NRMSE (train data)',...
                'NRMSE (validation data)','PRESS',...
                'AIC',...
                'R squared',...
                'PRESS R squared'};
            
            % "Globals"
            xachse  = cell(1,2); % 1. Colum: values for the 'left' x-axis;
                                 % 2. Colum: values for the 'right' x-axis;
            yachse  = xachse;
            yBeschr = xachse;
            xBeschr = xachse;
            plothandle = xachse;
            
            % Load element positions
            positionen = [];
            if isunix
                load('positionenTrain.mat');
            else
                load('positionenTrainWindows.mat');
            end
            
            % Figure
            fh.figureHandle = figure('Visible','off',...
                'Position',fhPos,...
                'MenuBar','none',...
                'Toolbar','figure',...
                'Color',bgcolor,...
                'KeyReleaseFcn',@reposElements);
            
            
            %% Model panel
            hMNpanel = uipanel('Parent',fh.figureHandle,...
                'Title','',...
                'Units','pixels',...
                'Position',positionen.MNpanel,...
                'Tag','MNpanel');
            
            %% Create untrained model
            hMNtextEins = uicontrol('Parent',hMNpanel,...
                'Style','text',...
                'Position',positionen.MNtextEins,...
                'String','Create (untrained) model',...
                'HorizontalAlignment','left',...
                'FontName',FNueber,...
                'FontSize',FSueber,...
                'Tag','MNtextEins');
            
            hMNtextTT = uicontrol('Parent',hMNpanel,...
                'Style','text',...
                'Position',positionen.MNtextTT,...
                'String','Choose training method:',...
                'HorizontalAlignment','left',...
                'FontName',fontname,...
                'FontSize',fontsize,...
                'Tag','MNtextTT');
            
            hMNtrainType = uicontrol('Parent',hMNpanel,...
                'Style','popupmenu',...
                'Value',1,...
                'String',{'HILOMOT','LOLIMOT'},...
                'Position',positionen.MNtrainType,...
                'Tag','MNtrainType');
            
            hMNbezText = uicontrol('Parent',hMNpanel,...
                'Style','text',...
                'Position',positionen.MNbezText,...
                'String','Model description:',...
                'HorizontalAlignment','left',...
                'FontName',fontname,...
                'FontSize',fontsize,...
                'Tag','MNbezText');
            
            hMNbezeichnung = uicontrol('Parent',hMNpanel,...
                'Style','edit',...
                'Position',positionen.MNbezeichnung,...
                'String','',...
                'Tag','MNbezeichnung');
            
            hMNbuttonCreate = uicontrol('Parent',hMNpanel,...
                'Style','pushbutton',...
                'Position',positionen.MNbuttonCreate,...
                'String','Create untrained model',...
                'Callback',@editBrowse,...
                'Tag','MNbuttonCreate');
            
            %% Choose or read in model
            hMNtextZwei = uicontrol('Parent',hMNpanel,...
                'Style','text',...
                'Position',positionen.MNtextZwei,...
                'String','Choose/read in model',...
                'HorizontalAlignment','left',...
                'FontName',FNueber,...
                'FontSize',FSueber,...
                'Tag','MNtextZwei');
            
            hMNeditModel = uicontrol('Parent',hMNpanel,...
                'Style','edit',...
                'Position',positionen.MNeditModel,...
                'String','',...
                'Callback',@editBrowse,...
                'Tag','MNeditModel');
            
            hMNbuttonBr = uicontrol('Parent',hMNpanel,...
                'Style','pushbutton',...
                'Position',positionen.MNbuttonBr,...
                'Callback',@editBrowse,...
                'String','Browse',...
                'Enable','on',...
                'Tag','MNbuttonBr');
            
            hMNpop = uicontrol('Parent',hMNpanel,...
                'Style','popupmenu',...
                'Value',1,...
                'String',{'No model available yet.'},...
                'Callback',@chooseLMN,...
                'Position',positionen.MNpopup,...
                'Tag','MNpopup');
            
            hMNbuttonLoesch = uicontrol('Parent',hMNpanel,...
                'Style','pushbutton',...
                'Position',positionen.MNbuttonLoesch,...
                'Callback',@deletePopupElement,...
                'String','Delete',...
                'Enable','on',...
                'Tag','MNbuttonLoesch');
            
            hMNbuttonErw = uicontrol('Parent',hMNpanel,...
                'Style','pushbutton',...
                'Position',positionen.MNbuttonErw,...
                'Callback',@erwEinstellungen,...
                'String','Advanced settings',...
                'Enable','on',...
                'Tag','MNbuttonErw');
            
            hMNbuttonSave = uicontrol('Parent',hMNpanel,...
                'Style','pushbutton',...
                'Position',positionen.MNbuttonSave,...
                'Callback',@saveLMN,...
                'String','Save',...
                'Enable','on',...
                'Tag','MNbuttonSave');
            
            hLMNgroup = [hMNbuttonLoesch,hMNbuttonErw,hMNbuttonSave];
            
            %% Choose or read in training data
            hMNtextDrei = uicontrol('Parent',hMNpanel,...
                'Style','text',...
                'Position',positionen.MNtextDrei,...
                'String','Choose/read in train data',...
                'HorizontalAlignment','left',...
                'FontName',FNueber,...
                'FontSize',FSueber,...
                'Tag','MNtextDrei');
            
            hMNeditTD = uicontrol('Parent',hMNpanel,...
                'Style','edit',...
                'Position',positionen.MNeditTD,...
                'String','',...
                'Callback',@editBrowse,...
                'Tag','MNeditTD');
            
            hMNbuttonTD = uicontrol('Parent',hMNpanel,...
                'Style','pushbutton',...
                'Position',positionen.MNbuttonTD,...
                'String','Browse',...
                'Callback',@editBrowse,...
                'Tag','MNbuttonTD');
            
            hTDpop = uicontrol('Parent',hMNpanel,...
                'Style','popupmenu',...
                'Value',1,...
                'String',{'No train data available yet.'},...
                'Position',positionen.TDpopup,...
                'Tag','TDpopup');
            
            hMNbuttonTDloesch = uicontrol('Parent',hMNpanel,...
                'Style','pushbutton',...
                'Position',positionen.MNbuttonTDloesch,...
                'String','Delete',...
                'Enable','on',...
                'Callback',@deletePopupElement,...
                'Tag','MNbuttonTDloesch');
            
            hMNbuttonTDbearbeiten = uicontrol('Parent',hMNpanel,...
                'Style','Pushbutton',...
                'String','Edit',...
                'Enable','on',...
                'Callback',@editDS,...
                'Position',positionen.TDbearbeiten,...
                'Tag','TDbearbeiten');
            
            hMNbuttonTDspeichern = uicontrol('Parent',hMNpanel,...
                'Style','Pushbutton',...
                'String','Save',...
                'Enable','on',...
                'Callback',@saveDS,...
                'Position',positionen.TDspeichern,...
                'Tag','TDspeichern');
            
            hTDgroup = [hMNbuttonTDloesch,hMNbuttonTDbearbeiten,...
                hMNbuttonTDspeichern];
            
            %% Begin training
            
            hStartTraining = uicontrol('Parent',hMNpanel,...
                'Style','Pushbutton',...
                'String','Train model',...
                'Enable','on',...
                'Callback',@startTrain,...
                'Position',positionen.StartTraining,...
                'Tag','StartTraining');
            
            hPlotPartition = uicontrol('Parent',hMNpanel,...
                'Style','Pushbutton',...
                'String','Edit model',...
                'Enable','on',...
                'Callback',@showPartitioning,...
                'Position',positionen.PlotPartition,...
                'Tag','PlotPartition');
            
            %% 
            hGRpanel = uipanel('Parent',fh.figureHandle,...
                'Title','Model validation criteria',...
                'Units','pixels',...
                'Position',positionen.GRpanel,...
                'Tag','GRpanel');
            
            hStartVisualizationButton = uicontrol('Parent',hGRpanel,...
                'Style','Pushbutton',...
                'String','Visualize model',...
                'Enable','on',...
                'Callback',@startVisualization,...
                'Position',positionen.startVisualization,...
                'Tag','startVisualization');
            
            haLeft = axes('Units','pixels',...
                'Parent',hGRpanel,...
                'Position',positionen.ha,...
                'XColor',leftcolor,...
                'YColor',leftcolor,...
                'YAxisLocation','left',...
                'XAxisLocation','bottom',...
                'Tag','ha');
            
            haRight = axes('Units','pixels',...
                'Parent',hGRpanel,...
                'Position',positionen.ha,...
                'YAxisLocation','right',...
                'XAxisLocation','top',...
                'Color','none',...
                'XColor',rightcolor,...
                'YColor',rightcolor,...
                'Tag','haR');
            
            cblog{1,1} = uicontrol(hGRpanel,...
                'Style','checkbox',...
                'String','logarithmic scale',...
                'Value',0,...
                'Position',positionen.logLeft,...
                'Callback',@axisScale,...
                'Tag','logLeft');
            
            cblog{2,1} = uicontrol(hGRpanel,...
                'Style','checkbox',...
                'String','logarithmic scale',...
                'Value',0,...
                'Position',positionen.logRight,...
                'Callback',@axisScale,...
                'Tag','logRight');
            
            hScaleSame = uicontrol('Parent',hGRpanel,...
                'Style','Pushbutton',...
                'String','Scale axes equally',...
                'Enable','on',...
                'Callback',@scaleAxes,...
                'Position',positionen.ButtonScaleSame,...
                'Tag','ButtonScaleSame');
            
            hScaleIndi = uicontrol('Parent',hGRpanel,...
                'Style','Pushbutton',...
                'String','Scale axes to particular range',...
                'Enable','on',...
                'Callback',@scaleAxes,...
                'Position',positionen.ButtonScaleIndi,...
                'Tag','ButtonScaleIndi');
            
            hExportFig = uicontrol('Parent',hGRpanel,...
                'Style','Pushbutton',...
                'String','Export figure',...
                'Enable','on',...
                'Callback',@exportFigure,...
                'Position',positionen.ButtonExportFigure,...
                'Tag','ButtonExportFigure');
            
            hKritPop = cell(2,1);
            
            hKritPop{1} = uicontrol('Parent',hGRpanel,...
                'Style','popupmenu',...
                'Value',1,...
                'String',kritList,...
                'Position',positionen.KritPopLeft,...
                'Callback',@chooseKrit,...
                'Tag','KritPopLeft');
            
            hKritPop{2} = uicontrol('Parent',hGRpanel,...
                'Style','popupmenu',...
                'Value',1,...
                'String',kritList,...
                'Position',positionen.KritPopRight,...
                'Callback',@chooseKrit,...
                'Tag','KritPopRight');
            
            %% Settings for model validation
            hValiText1 = uicontrol('Parent',hMNpanel,...
                'Style','text',...
                'Position',positionen.ValiText1,...
                'String','Model validation',...
                'HorizontalAlignment','left',...
                'FontName',FNueber,...
                'FontSize',FSueber,...
                'Tag','ValiText1');
            
            hValiText2 = uicontrol('Parent',hMNpanel,...
                'Style','text',...
                'Position',positionen.ValiText2,...
                'String','Load and select validation data:',...
                'HorizontalAlignment','left',...
                'FontName',fontname,...
                'FontSize',fontsize,...
                'Tag','ValiText2');
            
            hvaliEdit = uicontrol('Parent',hMNpanel,...
                'Style','edit',...
                'Position',positionen.valiEdit,...
                'String','',...
                'Callback',@editBrowse,...
                'Tag','valiEdit');
            
            hvaliButtonTD = uicontrol('Parent',hMNpanel,...
                'Style','pushbutton',...
                'Position',positionen.valiButtonTD,...
                'String','Browse',...
                'Callback',@editBrowse,...
                'Tag','valiButtonTD');
            
            hvaliTDpop = uicontrol('Parent',hMNpanel,...
                'Style','popupmenu',...
                'Value',1,...
                'String',{'No test data yet'},...
                'Callback',@chooseTestData,...
                'Position',positionen.valiTDpopup,...
                'Tag','valiTDpopup');
            
            hvaliButtonTDloesch = uicontrol('Parent',hMNpanel,...
                'Style','pushbutton',...
                'Position',positionen.valiButtonTDloesch,...
                'String','Delete',...
                'Enable','off',...
                'Callback',@deletePopupElement,...
                'Tag','valiButtonTDloesch');
            
            hvaliButtonTDbearbeiten = uicontrol('Parent',hMNpanel,...
                'Style','Pushbutton',...
                'String','Edit',...
                'Enable','off',...
                'Callback',@editDS,...
                'Position',positionen.valiTDbearbeiten,...
                'Tag','valiTDbearbeiten');
            
            hvaliButtonTDspeichern = uicontrol('Parent',hMNpanel,...
                'Style','Pushbutton',...
                'String','Save',...
                'Enable','off',...
                'Callback',@saveDS,...
                'Position',positionen.valiTDspeichern,...
                'Tag','valiTDspeichern');
            
            hvaliOptionsButton = uicontrol('Parent',hMNpanel,...
                'Style','Pushbutton',...
                'String','Cross validation options...',...
                'Enable','on',...
                'Callback',@editCrossValidationOptions,...
                'Position',positionen.valiOptionsButton,...
                'Tag','valiOptionsButton');
            
            hValiText3 = uicontrol('Parent',hMNpanel,...
                'Style','text',...
                'Position',positionen.ValiText3,...
                'String',['(The cross validation options affect the '...
                'output of all criteria with PRESS in its name)'],...
                'HorizontalAlignment','left',...
                'FontName',fontname,...
                'FontSize',fontsize,...
                'Tag','ValiText3');
            
            hvaliGroup = [hvaliButtonTDspeichern,...
                hvaliButtonTDbearbeiten,hvaliButtonTDloesch];
            
            % Set element's background-color
            allHandles = [get(fh.figureHandle,'Children');...
                get(hMNpanel,'Children');...
                get(hGRpanel,'Children')];
            [~,~,idxHA] = setxor([haLeft,haRight],allHandles);
            allBGhandles = allHandles(idxHA);
            set(allBGhandles,'BackgroundColor',bgcolor);
            
            % Set color of all 'white' elements
            set([hMNpop,hMNeditTD,hTDpop,hMNtrainType,hMNbezeichnung,...
                hMNeditModel,hKritPop{:},hGRpanel,hvaliEdit,...
                cblog{:},hvaliTDpop],...
                'BackgroundColor',white);
            
            % Make figure visible
            set(fh.figureHandle,'Visible','on');
            set([hLMNgroup,hTDgroup],'Enable','off');
            
            % Figure-initialization finished
            % check if input arguments has been passed
            if nargin > 0
                % If input arguments has been passed, create entry within
                % the model popup-menu
                fh.LMNelements              = fh.LMNelements + 1;
                fh.model{fh.LMNelements,1}  = modelObject;
                fh.model{fh.LMNelements,2}  = GUItrain.setCVoptions();
                
                set(hMNpop,'Value',fh.LMNelements);
                updatePopup('LMN');
            end
            
            %% GUItrain functions
            
            function startVisualization(hObject,eventdata)
                % Check if there are already trained models. If so, start
                % visualizationGUI with all already trained models
                size(hObject);
                size(eventdata);
                
                % Check if there are already models
                if ~isempty(fh.model)
                    idx         = false(size(fh.model,1),1);
                    lmns        = cell(size(fh.model,1),1);
                    for jj = 1:size(fh.model,1)
                        lmns{jj,1}    = fh.model{jj,1};
                        % Check if lmn is trained
                        if ~isempty(lmns{jj,1}.leafModels)
                            % Get all indices of already trained models
                            % within the cell-array fh.model
                            idx(jj,1)   = true;
                        end
                    end
                    
                    % Be sure there are only trained model objects within the
                    % cell array lmns
                    lmns        = lmns(idx,:);
                    
                    % Check if there is any trained model
                    if size(lmns,1) > 0
                        % Open GUI initialized with all trained lmn-objects
%                         visualizationGUI.compareModels(lmns{:});
                        GUIvisualize(lmns{:});
                    else
%                         visualizationGUI.compareModels;
                        GUIvisualize;
                    end
                end
                
            end % end startVisualization
            
            function axisScale(hObject,eventdata)
                % function axisScale is called, if the user is about to
                % change the scaling of the y-axes of one of the two axis.
                size(eventdata);
                
                if hObject == cblog{1,1}
                    updatePlot(hKritPop{1});
                elseif hObject == cblog{2,1}
                    updatePlot(hKritPop{2});
                end
            end % end axisScale
            
            function chooseTestData(hObject,eventdata)
                % This function is called if the user changed the test
                % dataset object, that should be displayed.
                size(hObject);
                size(eventdata);
                updatePlot(hKritPop{1},hKritPop{2});
            end % end chooseTestData
            
            function editCrossValidationOptions(hObject,eventdata)
                % Callback-function opens a GUI to set options for the
                % cross validation
                size(hObject);
                size(eventdata);
                if isempty(fh.model)
                    msgbox('No model existing yet.','Error','error');
                    return;
                else
                    LMNno = get(hMNpop,'Value');
                    LMN   = fh.model{LMNno,1};
                    
                    % Save crossvalidation options and, if necessary, save
                    % calculated crossvalidation values to the model (LMN)
                    [fh.model{LMNno,2},fh.showCVwarning,LMN] = ...
                        GUItrain.crossvalidationGUI(LMN,fh.model{LMNno,2},fh.showCVwarning);
                    
                    fh.model{LMNno,1} = LMN;
                    updatePlot(hKritPop{1},hKritPop{2});
                end
            end % end editCrossValidationOptions
            
            function exportFigure(hObject,eventdata)
                % Opens the displayed graphs in a new window
                
                size(hObject);
                size(eventdata);
                if isempty(yachse{1,1}) && isempty(yachse{1,2})
                    return;
                else
                    figure;
                    if ~isempty(yachse{1,1}) && ~isempty(yachse{1,2})
                        ax1 = axes;
                        ax2 = axes;
                        
                        xlim1 = get(haLeft,'XLim');
                        ylim1 = get(haLeft,'YLim');
                        xlim2 = get(haRight,'XLim');
                        ylim2 = get(haRight,'YLim');
                        
                        
                        plot(ax1,xachse{1,1},yachse{1,1},'LineWidth',...
                            linewidth,'Color',leftcolor,'LineStyle',...
                            leftline);
                        plot(ax2,xachse{1,2},yachse{1,2},'LineWidth',...
                            linewidth,'Color',rightcolor,'LineStyle',...
                            rightline);
                        
                        set(ax1,'XLim',xlim1,'YLim',ylim1);
                        set([ax1,ax2],'Box','off');
                        
                        hx1 = xlabel(ax1,xBeschr{1,1});
                        hy1 = ylabel(ax1,yBeschr{1,1});
                        hx2 = xlabel(ax2,xBeschr{1,2});
                        hy2 = ylabel(ax2,yBeschr{1,2});
                        
                        set([hx1,hy1,hx2,hy2],'FontName',fontname,...
                            'FontSize',fontsize);
                        
                        ax1pos = get(ax1,'Position');
                        set(ax2,'YAxisLocation','Right','XAxisLocation',...
                            'Top','color','none','XLim',xlim2,'YLim',...
                            ylim2,'XColor',rightcolor,'YColor',rightcolor,...
                            'Position',ax1pos);
                    elseif ~isempty(yachse{1,2}) || ~isempty(yachse{1,1})
                        ax1 = axes;
                        if ~isempty(yachse{1,1})
                            xlim = get(haLeft,'XLim');
                            ylim = get(haLeft,'YLim');
                            x    = xachse{1,1};
                            y    = yachse{1,1};
                            color= leftcolor;
                            xlab = xBeschr{1,1};
                            ylab = yBeschr{1,1};
                        else
                            xlim = get(haRight,'XLim');
                            ylim = get(haRight,'YLim');
                            x    = xachse{1,2};
                            y    = yachse{1,2};
                            color= rightcolor;
                            xlab = xBeschr{1,2};
                            ylab = yBeschr{1,2};
                        end
                        
                        plot(ax1,x,y,'LineWidth',linewidth,'Color',color);
                        set(ax1,'XLim',xlim,'YLim',ylim);
                        hx = xlabel(ax1,xlab);
                        hy = ylabel(ax1,ylab);
                        set([hx,hy],'FontName',fontname,...
                            'FontSize',fontsize);
                    end
                end
                
            end % end exportFigure
            
            function scaleAxes(hObject,eventdata)
                % Function scales both axes either to the same or to their
                % specific ranges.
                size(eventdata);
                if ~isempty(yachse{1,1}) && ~isempty(yachse{1,2})
                    maxy1  = max(yachse{1,1});
                    maxy2  = max(yachse{1,2});
                    miny1  = min(yachse{1,1});
                    miny2  = min(yachse{1,2});
                    range1 = maxy1 - miny1;
                    range2 = maxy2 - miny2;
                    proz   = 0.01;
                    
                    miny1 = (miny1 - proz*range1);
                    maxy1 = (maxy1 + proz*range1);
                    
                    miny2 = (miny2 - proz*range2);
                    maxy2 = (maxy2 + proz*range2);
                    if hObject == hScaleSame
                        miny1 = min([miny1;miny2]);
                        maxy1 = max([maxy1;maxy2]);
                        
                        
                        miny2 = miny1;
                        maxy2 = maxy1;
                    end
                    set(haLeft,'YLim',[miny1 maxy1]);
                    set(haRight,'YLim',[miny2 maxy2]);
                else
                    return;
                end
            end % end scaleAxes
            
            function showPartitioning(hObject,eventdata)
                % Calls the GUI to display the partitioning of the model
                % object
                size(hObject);
                size(eventdata);
                
                % Check if there are already models 
                if ~isempty(fh.model)
                    
                    MN = get(hMNpop,'Value');
                    if ~isempty(fh.model{MN,1}.leafModels)
                        fh.model{MN,1} = GUIvisualize.plotPartitionGUI(fh.model{MN,1});
                    else
                        msgbox(['Model has not been trained yet. So ',...
                            'there is no partitioning to plot...'],...
                            'Error','error');
                    end
                end
            end % end showPartitioning
            
            function chooseKrit(hObject,eventdata)
                % If the user changed one of the two criteria, that should
                % be displayed within the axis. That axis has to be updated
                size(eventdata);
                    
                updatePlot(hObject);
            end % end chooseKrit
            
            function chooseLMN(varargin)
                % This function is called, if the user changed the selected
                % model. Therefor the plots have to be updated.
                size(varargin);
                
                updatePlot(hKritPop{1},hKritPop{2});
            end % end chooseLMN
            
            
            function updatePlot(varargin)
                % Ploting of the criteria
                % 
                % updatePlot(axisHandle,[axisHandle2])
                %
                % axisHandle:   Handle to axis, that should be updated
                %
                % It is possible to pass both or only one of the two axis
                % handles to this function.
                
                if isempty(fh.model)
                    return;
                else
                    LMNno = get(hMNpop,'Value');
                    lmn   = fh.model{LMNno,1};
                end
                
                if ~isempty(lmn.leafModels)
                    
                    for ii = 1:size(varargin,2)
                        % Determine if the 'left' or 'right' axis should be
                        % updated
                        [~,~,j] = intersect(varargin{ii},cell2mat(hKritPop));
                        
                        % Get the criterium, that should be plotted
                        tmpNo   = get(varargin{ii},'Value');
                        tmp     = get(varargin{ii},'String');
                        tmpKrit = tmp{tmpNo};
                        
                        % Pass plot information to 'global' variables
                        switch tmpKrit
                            case 'NRMSE (train data)'
                                
                                % define x-axis
                                xachse{j} = ...
                                    1:size(lmn.history.leafModelIter,2);
                                
                                % pre-define y-axis
                                yachse{j} = zeros(size(xachse{j}));
                                
                                % Get the NRMSE-values of all model
                                % complexities
                                for complexity = xachse{j}
                                    % set current model complexity
                                    tmp = lmn;
                                    tmp.leafModels = ...
                                        tmp.history.leafModelIter{complexity};
                                    
                                    % evaluate model output of the
                                    % current model
                                    yModel = tmp.calculateModelOutput(tmp.unscaledInput,tmp.unscaledOutput);
                                    
                                    % calculate the current NRMSE-value
                                    yachse{j}(complexity) = tmp.calcGlobalLossFunction(tmp.output, yModel, lmn.dataWeighting, lmn.outputWeighting, 'NRMSE');
                                end
                                
                                % set x- and y-label
                                yBeschr{j} = 'NRMSE (train data)';
                                xBeschr{j} = 'Number of local models';
                            case 'R squared'
                                [xachse{j} yachse{j}] = GUItrain.getCriterion(lmn,'R2');
                                yBeschr{j} = 'Coefficient of determination (R^2)';
                                xBeschr{j} = 'Number of local models';
                            case 'Adjusted R squared'
                                [xachse{j} yachse{j}] = GUItrain.getCriterion(lmn,'aR2');
                                yBeschr{j} = ...
                                    'Adjusted R^2';
                                xBeschr{j} = 'Number of local models';
                            case 'PRESS R squared'
                                % show warning because of the time
                                % consuming calculation
                                if fh.showCVwarning == 1
                                    [flag,fh.showCVwarning] = ...
                                        GUItrain.crossValidationWarning;
                                else
                                    flag        = 1;
                                end
                                
                                if flag
                                    % Check if crossvalidation evaluation
                                    % is already performed for desired
                                    % model complexities
                                    modelComplexities = fh.model{LMNno,2}.modelComplexities;
                                    if size(lmn.history.kFoldCVlossFunction,2) < max(modelComplexities)
                                        idx = 1;
                                    else
                                        idx = lmn.history.kFoldCVlossFunction(modelComplexities)==0;
                                    end
                                    
                                    if sum(idx) > 0
                                        % evaluate the sum squared cross validation
                                        % values
                                        lmn.kFold = fh.model{LMNno,2}.numberOfBatches;
                                        lmn.history.kFoldCVlossFunction = NaN(1,max(modelComplexities));
                                        lmn.history.kFoldCVlossFunction(modelComplexities) = lmn.crossvalidation(fh.model{LMNno,2}.numberOfBatches,modelComplexities);
%                                         lmn        = lmn.crossvalidation(lmn,fh.model{LMNno,2}.numberOfBatches,modelComplexities);
                                    end
                                    
                                    % save the calculated cross validation
                                    % errors to the model-object to avoid
                                    % timecomsuming re-calculation
                                    fh.model{LMNno,1} = lmn;
                                    xachse{j}  = fh.model{LMNno,2}.modelComplexities;
                                    yachse{j}  = lmn.history.kFoldCVlossFunction(xachse{j});
                                    yBeschr{j} = 'PRESS R squared';
                                    xBeschr{j} = 'Number of local models';
                                    
                                    varY = sum((lmn.output-mean(lmn.output)).^2);
                                    
                                    % now calculate the RsquaredPRESS values
                                    % Calculate the RsquaredPRESS value for each model complexity
                                    for jj = [xachse{j};1:size(xachse{j},2)]
                                        
                                        numerator = varY - yachse{j}(jj(2));
                                        yachse{j}(jj(2)) = numerator/varY;
                                    end
                                else
                                    return;
                                end
                            case 'AIC'
                                [xachse{j} yachse{j}] = GUItrain.getCriterion(lmn,'AIC');
                                yBeschr{j} = ...
                                    'AIC';
                                xBeschr{j} = 'Number of local models';
                                
                            case 'PRESS'
                                % show warning because of the time
                                % consuming calculation
                                if fh.showCVwarning == 1
                                    [flag,fh.showCVwarning] = ...
                                        GUItrain.crossValidationWarning;
                                else
                                    flag        = 1;
                                end
                                
                                if flag
                                    % Check if crossvalidation evaluation
                                    % is already performed for desired
                                    % model complexities
                                    modelComplexities = fh.model{LMNno,2}.modelComplexities;
                                    if size(lmn.history.kFoldCVlossFunction,2) < max(modelComplexities)
                                        idx = 1;
                                    else
                                        idx = lmn.history.kFoldCVlossFunction(modelComplexities)==0;
                                    end
                                    
                                    if sum(idx) > 0
                                        % evaluate the sum squared cross validation
                                        % values
                                        lmn.kFold = fh.model{LMNno,2}.numberOfBatches;
                                        lmn.history.kFoldCVlossFunction = NaN(1,max(modelComplexities));
                                        lmn.history.kFoldCVlossFunction(modelComplexities) = lmn.crossvalidation(fh.model{LMNno,2}.numberOfBatches,modelComplexities);
                                    end
                                    
                                    % save the calculated cross validation
                                    % errors to the model-object to avoid
                                    % timecomsuming re-calculation
                                    fh.model{LMNno,1} = lmn;
                                    xachse{j}  = fh.model{LMNno,2}.modelComplexities;
                                    yachse{j}  = lmn.history.kFoldCVlossFunction(xachse{j});
                                    yBeschr{j} = 'PRESS';
                                    xBeschr{j} = 'Number of local models';
                                else
                                    return;
                                end
                            case 'None'
                                if ~isempty(plothandle{j})
                                    delete(plothandle{j});
                                    plothandle{j} = [];
                                end
                                return;
                            case 'NRMSE (validation data)'
                                if ~isempty(fh.testData)
                                    % determine which test dataset is
                                    % selected
                                    TDno    = get(hvaliTDpop,'Value');
                                    TD      = fh.testData{TDno,1};
                                    
                                    % check if the size of the input space
                                    % of the test dataset matches the one
                                    % with which the selected model was
                                    % trained. If not --> return!!
                                    if size(TD.input,2) ~= ...
                                            size(lmn.input,2)
                                        warning(['Different ',...
                                            'input spaces ',...
                                            'between the test- and ',...
                                            'train-dataset. No error ',...
                                            'will be calculated!']);
                                        return;
                                    end
                                    
                                    % Normalization to the codomain of the
                                    % train dataset, if it is normalized
%                                     if ~isempty(lmn.scaleInput)
%                                         % generate normalized data-matrix
%                                         input = lmn.scaleInput.scale(TD.unscaledInput);
%                                     else
%                                         input = TD.unscaledInput;
%                                     end
%                                     if ~isempty(lmn.scaleOutput)
%                                         % generate normalized data-matrix
%                                         output = lmn.scaleOutput.scale(TD.unscaledOutput);
%                                     else
%                                         output = TD.unscaledOutput;
%                                     end
                                    
                                    % define x-axis
                                    xachse{j} = 1:size(lmn.history.leafModelIter,2);
                                    
                                    % pre-define y-axis
                                    yachse{j} = zeros(size(xachse{j}));
                                    
                                    % Get the NRMSE-values of all model
                                    % complexities
                                    tmp = lmn;
                                    for complexity = xachse{j}
                                        % set current model complexity
                                        tmp.leafModels = tmp.history.leafModelIter{complexity};
                                        
                                        % evaluate model output of the
                                        % current model
                                        yModel = tmp.calculateModelOutput(TD.unscaledInput,TD.unscaledOutput);
                                        
                                        % calculate the current NRMSE-value
                                        if isempty(tmp.scaleOutput)
                                            yachse{j}(complexity) = tmp.calcGlobalLossFunction(TD.output,yModel,TD.dataWeighting,TD.outputWeighting,'NRMSE');
                                        else
                                            yachse{j}(complexity) = tmp.calcGlobalLossFunction(TD.unscaledOutput,yModel,TD.dataWeighting,TD.outputWeighting,'NRMSE');
                                        end
                                    end
                                    
                                    % set x- and y-label
                                    yBeschr{j} = 'NRMSE (validation data)';
                                    xBeschr{j} = 'Number of local models';
                                end
                        end
                        plotKrit(j);
                    end
                else
                    return;
                end
            end % end getPlotData
            
            
            function plotKrit(flag)
                % Plots the values, that are currently saved within the
                % global variables 'xachse' and 'yachse', into the chosen
                % axis
                
                if flag == 1
                    % "Left" axis
                    achse = haLeft;
                    logScale = get(cblog{1,1},'Value');
                elseif flag == 2
                    % "Right" axis
                    achse = haRight;
                    logScale = get(cblog{2,1},'Value');
                end
                
                if logScale
                    logScale = 'log';
                else
                    logScale = 'linear';
                end
                
                hold(achse,'off');
                plothandle{flag} = plot(achse,xachse{flag},yachse{flag},...
                    'LineWidth',linewidth,...
                    'Color',pcolor(flag,:),...
                    'LineStyle',pline{flag});
                hx = xlabel(achse,xBeschr{flag});
                hy = ylabel(achse,yBeschr{flag});
                set([hx,hy],'FontSize',fontsize,...
                    'FontName',fontname);
                
                % Refresh axis settings (lost after plot refresh)
                set(achse,'Box','off','YColor',pcolor(flag,:),...
                    'XColor',pcolor(flag,:));
                if flag == 2
                    set(achse,'YAxisLocation','Right',...
                        'XAxisLocation','Top','color','none');
                end
                set(achse,'YScale',logScale);
            end % plotKrit
            
            
            function startTrain(hObject,eventdata)
                % The selected model will be trained
                size(hObject);
                size(eventdata);
                if ~isempty(fh.model) && ~isempty(fh.trainData)
                    % Read in training dataset and (untrained) model
                    LMNno = get(hMNpop,'Value');
                    TDno  = get(hTDpop,'Value');
                    LMN   = fh.model{LMNno,1};
                    TD    = fh.trainData{TDno,1};
                    
                    % If the chosen model is already trained, the training
                    % has to be deleted
                    if ~isempty(LMN.leafModels)
                        % Delete already existing training
                        LMN.history         = modelHistory;
                        LMN.leafModels      = [];
                        LMN.localModels     = [];
                        LMN.outputModel     = [];
                        LMN.xRegressor      = [];
                        LMN.zRegressor      = [];
                        classname              = class(LMN);
                        switch classname
                            case 'hilomot'
                                LMN.phi     = [];
                            case 'lolimot'
                                LMN.MSFValue= {[]};
                        end
                    end
                    
                    % Set the train dataset to the untrained model
                    LMN.input   = TD.unscaledInput;
                    LMN.output  = TD.unscaledOutput; 
                    
                    % Pass all dataset information to the model object
                    LMN.info            = TD.info;
                    LMN.dataWeighting   = TD.dataWeighting;
                    LMN.outputWeighting = TD.outputWeighting;
                    
                    
                    % Model training
                    fh.model{LMNno,1} = ...
                        LMN.train(fh.showTrainingSuccess);
                    fh.model{LMNno,2} = ...
                        GUItrain.setCVoptions(2,1:size(fh.model{LMNno,1}.history.leafModelIter,2));
                    
                    % Show results
                    updatePlot(hKritPop{1},hKritPop{2});
                else
                    msgbox(['Make sure you choose a dataset as well',...
                        ' as a model.'],'Error','error');
                end
            end % end startTrain
            
            function erwEinstellungen(varargin)
                % Callback-function opens GUI to set additional options for
                % the model training
                
                size(varargin);
                value = get(hMNpop,'Value');
                [fh.model{value,1},fh.showTrainingSuccess] = ...
                    GUItrain.editTrainOptions(fh.model{value,1},fh.showTrainingSuccess);
            end % end erwEinstellungen
            
            function saveLMN(hObject,eventdata)
                size(hObject);
                size(eventdata);
                
                % Distinction which model object should be saved
                [~,~,LMN] = intersect(hObject,hMNbuttonSave);
                switch LMN
                    case 1
                        [file,filepath] = uiputfile('*.mat');
                        if file
                            speichername = [filepath,file];
                        else
                            return;
                        end
                        
                        value = get(hMNpop,'Value');
                        model = fh.model{value,1}; %#ok<PROP,NASGU>
                        save(speichername,'model');
                        
                end
            end % end saveLMN
            
            function updatePopup(quelle)
                % Refresh entries within a popup element
                switch quelle
                    case 'LMN'
                        % Popup-Menue der Modelle updaten
                        [names fh.model] = searchDEL(fh.LMNelements,...
                            fh.model);
                        fh.LMNelements = size(names,1);
                        
                        setPopupMenu(names,hMNpop,...
                            fh.LMNelements,hLMNgroup)
                    case 'TD'
                        % update train data popup menu
                        [names fh.trainData] = searchDEL(fh.TDelements,...
                            fh.trainData);
                        fh.TDelements = size(names,1);
                        
                        setPopupMenu(names,hTDpop,...
                            fh.TDelements,hTDgroup)
                    case 'testData'
                        % update test data popup menu
                        [names fh.testData] = ...
                            searchDEL(fh.testDataElements,fh.testData);
                        fh.testDataElements = size(names,1);
                        
                        setPopupMenu(names,hvaliTDpop,...
                            fh.testDataElements,hvaliGroup)
                end
                
                function [namesNew dataNew] = searchDEL(noElements,data)
                    % Determines all existing elements with corresponding
                    % names
                    namesNew = cell(noElements,1);
                    idx      = true(size(namesNew));
                    for ii = 1:noElements
                        if ~isempty(data{ii,1})
%                             klasse = class(data{ii,1});
%                             if strcmp(klasse,'lmnGUIBuild')
%                                 namesNew{ii,1} = data{ii,1}.name;
%                             else
                                namesNew{ii,1} = data{ii,1}.info.dataSetDescription;
%                             end
                        else
                            idx(ii) = false;
                        end
                    end
                    dataNew  = data(idx,:);
                    namesNew = namesNew(idx,:);
                end % end searchDEL
                
                function setPopupMenu(names,hpopup,noElements,varargin)
                    % Function changes the entries of an popup element
                    %
                    % setPopupMenu(names,hpopup,noElements,handleCell)
                    %
                    % names:        Cell-array containing all entries, that
                    %               should appear a popup menu
                    % hpopup:       Handle to the popup element, which
                    %               entries should be refreshed
                    % noElements:   Number of all popup entries
                    % handleCell:   Cell-array containing handles to
                    %               elements, that should be activated or
                    %               deactivated
                    
                    if isempty(names)
                        names = 'No data available at present.';
                        set([varargin{:}],'Enable','off');
                    else
                        set([varargin{:}],'Enable','on');
                        DSno = get(hpopup,'Value');
                        if DSno > noElements
                            set(hpopup,'Value',noElements);
                        end
                    end
                    set(hpopup,'String',names);
                end % end setPopupMenu
            end % end updatePopup
            
            function editDS(hObject,eventdata)
                % Callback-function calls GUI to edit options related to
                % a dataset object
                size(eventdata);
                
                quellen = ...
                    [hMNbuttonTDbearbeiten,hvaliButtonTDbearbeiten];
                
                % Determine which dataset object should be edited
                [~,~,DS] = intersect(hObject,quellen);
                switch DS
                    case 1
                        value                 = get(hTDpop,'Value');
                        fh.trainData{value,1} = ...
                            editDataSet(fh.trainData{value,1});
                        updatePopup('TD');
                    case 2
                        value                 = get(hvaliTDpop,'Value');
                        fh.testData{value,1} = ...
                            editDataSet(fh.testData{value,1});
                        updatePopup('testData');
                end
            end % end editDS
            
            function saveDS(hObject,eventdata)
                size(hObject);
                size(eventdata);
                
                quellen = ...
                    [hMNbuttonTDspeichern,hvaliButtonTDspeichern];
                
                % Distinction which dataset object should be saved
                [~,~,DS] = intersect(hObject,quellen);
                switch DS
                    case 1
                        value = get(hTDpop,'Value');
                        saveDataSet(fh.trainData{value,1});
                    case 2
                        value = get(hvaliTDpop,'Value');
                        saveDataSet(fh.testData{value,1});
                end
            end % end saveDS
            
            function deletePopupElement(hObject,eventdata)
                % This function deletes a model, a train-dataset or a
                % test-dataset, depending on the button, that calls this
                % function (hObject)
                size(hObject);
                size(eventdata);
                
                % Possible source-button, that can call this function
                % together with the corresponding handles to the popups,
                % that contain the objects, that should be deleted
                quellen = ...
                    [hMNbuttonTDloesch,hMNbuttonLoesch,hvaliButtonTDloesch;...
                    hTDpop,hMNpop,hvaliTDpop];
                
                % Get the colum within the variable 'quellen', that contain
                % the handles of interest
                [~,~,popupMenu] = intersect(hObject,quellen(1,:));
                
                % Delete the chosen element
                value = get(quellen(2,popupMenu),'Value');
                switch popupMenu
                    case 2
                        % Deleting the chosen model
                        fh.model{value,1} = [];
                        updatePopup('LMN');
                    case 1
                        % Deleting the chosen train-dataset
                        fh.trainData{value,1} = [];
                        updatePopup('TD');
                    case 3
                        % Deleting the chosen test-dataset
                        fh.testData{value,1} = [];
                        updatePopup('testData');
                end % end switch
            end % end deletePopupElement
            
            function editBrowse(hObject,eventdata)
                % Function reads one of the following elements into the
                % GUI: model object, train-dataset object or test-dataset
                % object
                size(eventdata);
                
                % Distinction if the function is called by an edit-field or
                % by an button
                info = get(hObject,'Style');
                if strcmp(info,'edit')
                    text = get(hObject,'String');
                    if isempty(strfind(text,'.'))
                        text = [text,'.mat'];
                    end
                elseif hObject == hMNbuttonCreate
                    text = [];
                elseif strcmp(info,'pushbutton')
                    [file,filepath] = uigetfile('*.mat');
                    if file
                        text = [filepath,file];
                    else
                        return;
                    end
                end
                
                
                % Processing of the string 'text' depending on the element,
                % that called this function
                if hObject == hMNeditModel || hObject == hMNbuttonBr
                    % Create/read in model
                    fh.LMNelements = fh.LMNelements + 1;
                    lmn     = load(text);
                    fh.model{fh.LMNelements,1} = lmn.model;
                    fh.model{fh.LMNelements,2} = GUItrain.setCVoptions;
                    
%                     fh.model{fh.LMNelements,1} = ...
%                         lmnGUIBuild.loadGUIlmn(text);

                    % Select the new model
                    set(hMNpop,'Value',fh.LMNelements);
                    updatePopup('LMN');
                    set(hMNeditModel,'String',text);
                    chooseLMN;
                elseif hObject == hMNbuttonCreate
                    % Create new (untrained) model
                    fh.LMNelements = fh.LMNelements + 1;
                    bezeichnung    = get(hMNbezeichnung,'String');
                    TTval          = get(hMNtrainType,'Value');
                    TTcell         = get(hMNtrainType,'String');
                    TT             = lower(TTcell{TTval});
                    if isempty(bezeichnung)
                        bezeichnung = [TT,' model'];
                    end
                    
                    eval(['lmn=',TT,';']);
                    lmn.info.dataSetDescription = bezeichnung;
                    fh.model{fh.LMNelements,1} = lmn;
                    
                    set(hMNpop,'Value',fh.LMNelements);
                    updatePopup('LMN');
                    
                    % If the train dataset is already chosen, pass it to
                    % the local model object
                    if fh.TDelements
                        TDno = get(hTDpop,'Value');
                        fh.model{fh.LMNelements,1} = ...
                            GUItrain.dataSet2model(fh.model{fh.LMNelements,1},...
                            fh.trainData{TDno,1});
                    end
                elseif hObject == hMNeditTD || hObject == hMNbuttonTD
                    % Read training data
                    fh.TDelements = fh.TDelements + 1;
                    fh.trainData{fh.TDelements,1} = ...
                        dataSet.loadDataSet(text);
                    set(hTDpop,'Value',fh.TDelements);
                    updatePopup('TD');
                    set(hMNeditTD,'String',text);
                elseif hObject == hvaliButtonTD || hObject == hvaliEdit
                    % Read test data
                    fh.testDataElements = fh.testDataElements + 1;
                    fh.testData{fh.testDataElements,1} = ...
                        dataSet.loadDataSet(text);
                    set(hvaliTDpop,'Value',fh.testDataElements);
                    updatePopup('testData');
                    set(hvaliEdit,'String',text);
                    updatePlot(hKritPop{1},hKritPop{2});
                end
                
            end % end editBrowse
            
            function reposElements(hObject,eventdata)
                % This function allows you to reposition GUI elements
                % simply
                
                size(hObject);
                if strcmp(eventdata.Key,'r')
                    set(allHandles,'Visible','on');
                    reposGUIelements(allHandles,fh.figureHandle);
                end
            end % end reposElements
            
        end % end constructor
    end
    
    methods(Static=true)
%         function object = loadGUIlmn(filename)
%             lmn         = load(filename);
%             trainType   = class(lmn.net);
%             
%             object      = lmnGUIBuild(trainType,lmn.name);
%             object.net  = lmn.net;
%             object.displayMode = lmn.displayMode;
%         end % end loadGUIlmn
        
        [flag,showWarning] = crossValidationWarning;
        cvOptions          = setCVoptions(numberOfBatches,modelComplexities);
        [outStruct,warningFlag,obj] = crossvalidationGUI(obj,inStruct,warningFlag);
        [x,y]              = getCriterion(modelObject,criterion);
        [modelObject,showTraining] = editTrainOptions(modelObject,showTraining);
        modelObject        = dataSet2model(modelObject,dataSetObject);
    end
    
end

