function [model,stats] = utl_searchmodel(varargin)
% Find the best predictive model out of a parameterized set, via cross-validation.
% [Model,Stats] = utl_searchmodel(Data, Arguments...)
%
% When a predictive model is learned from some data, several of its key parameters can usually be
% efficiently optimized given the data (namely, if their solutions are available in closed form, or
% if they can be found by solving a well-behaved optimization problem). Some parameters, however,
% usually remain, which can not be optimized by the given learning function. If their optimal values
% are unknown but assumed to lie in a small set of possibilities, a general-purpose approach to find
% them as well, is exhaustive search over all possibilities, coupled with a cross-validation to
% assess the predictive performance for each possibility on the available data.
%
% This function performs this task, and can jointly optimize all unknown parameters of a learning
% function. It requires a data set and (because a cross-validation is implicitly performed) all
% parameters that would be required to apply utl_crossval to the given data. The function whose
% parameters shall be optimized is the 'trainer' (i.e., model learning) function. For any of its
% parameters which has multiple possibilities (over which to optimize), these possibilities can be
% specified using the same syntax as in the grid searching function utl_gridsearch (where the
% default here is to specify them via search() clauses).
%
% In:
%   Data :   some data that can be partitioned using index sets
%
%   Arguments : mandatory arguments:
%               'trainer': training function; receives a partition of the data (as produced by
%                          the partitioner), some further arguments (as specified in args), and
%                          returns a model (of any kind)
%
%               'tester' : testing function; receives a model (as produced by the trainer) and a
%                          partition of the data (as produced by the partitioner), and returns a
%                          prediction for every index of the input data, in a format allowed by
%                          ml_predict
%
%               'args': arguments to the training function, cell array (default: empty)
%                       specified as in utl_gridsearch (format controlled via argform)
%
%               optional arguments (same as in utl_crossval, listed below for convenience)
%               'scheme': cross-validation scheme, can be one of the following: (default: 10)
%                 * 0: skip CV, return NaN and empty statistics
%                 * k: k-fold randomized CV (or, if 0<k<1, k-holdhout CV)
%                 * [r k]: r-time repartitioned k-fold randomized CV (or, if 0<k<1,
%                   k-holdout CV (with k a fraction))
%                 * 'loo': leave-one-out CV
%                 * {'chron', k} or {'block', k}: k-fold chronological/blockwise CV
%                 * {'chron', k, m} or {'block', k, m}: k-fold chronological/blockwise CV with
%                   m indices margin width (between training and test set)
%
%               'partitioner': partitioning function for the data, receives three parameters:
%                              (data, index vector, packed trainer args OR model)
%                               * if the index vector is empty, should return the highest index
%                                 in the data
%                               * otherwise, it should return data subindexed by the index vector
%                              default: provides support for cell arrays, numeric arrays, struct
%                                       arrays and {Data,Target} cell arrays
%                              note: the third parameter is for convenience and may optionally be
%                                    taken into account; trainer args are passed packed into a cell
%                                    array for both index set generation and computation of the
%                                    training partition(s), and the model is passed for the
%                                    computation of testing partitions
%
%               'target': a function to derive the target variable from a partition of the data (as
%                         produced by the partitioner), for evaluation; the allowed format is
%                         anything that may be output by ml_predict default: provides support for
%                         {Data,Target} cell arrays
%
%               'metric': metric to be employed, applied both in each fold and the aggregated data
%                         over all folds
%                          * function handle: a custom, user-supplied loss function; receives target
%                            data in the first argument and prediction data in the second argument;
%                            each can be in any format that can be produced by ml_predict (but can
%                            be expected to be mutually consistent). shall return a real number
%                            indicating the summary metric over all data, and optionally additional
%                            statistics in a struct
%                          * string: use ml_calcloss, with 'metric' determining the loss type
%                          * default: use 'mcr','mse','nll','kld', depending on supplied target &
%                            prediction data formats
%
%               'argform': format of the argument ranges, either 'direct' or 'clauses'
%                          (default: 'clauses')
%                           'direct': search ranges are directly specified as arrays
%                           'clauses': search ranges are specified using the search() clause
%
%               'forcestats': enforce a cross-validation to obtain stats, even when no search is
%                             necessary (default: 0)
%
%               further arguments (same as in utl_crossval)
%               'cache_fold_results' : whether to cache the per-fold results. (default: false)
%
%               'only_cached_results' : load only results that are in the cache. (default: false)
%
%               'tolerate_exceptions' : tolerate exceptions during training step. (default: false)
%
%               further arguments (same as in par_beginschedule, listed below for convenience)
%               'engine_gs': the parallelization engine to be used for the grid search
%                            (default: 'local')
%
%               'engine_ncv': the parallelization engine to be used for the nested cross-validation
%                             (default: 'global')
%
%               'pool'     : node pool to be used for parallelization, when using the BLS scheduler
%                            (default: 'global')
%
%               'policy'   : scheduling policy to be used, when using the BLS scheduler
%                            (default: 'global')
%
% Out:
%   Model : the best model, as determined through cross-validation and and grid search
%           w.r.t. to a measure of the overall performance of the trainer/tester combination
%
%   Stats : additional statistics over all tested argument combinations, as produced by the metric
%
% See also:
%   utl_crossval, utl_gridsearch, utl_nested_crossval
%
% Example:
%   % assuming a feature matrix called trials and a label vector called targets, sized as:
%   %  trials: [NxF] array of training instances
%   %  targets: [Nx1] array of labels
%
%   % find the best-performing SVM classifier on the data (here: ignoring the kernel scale parameter)
%   [model,stats] = utl_searchmodel({trials,targets}, 'args',{{'svm' search(2.^(-5:2:15))}})
%
%                                Christian Kothe, Swartz Center for Computational Neuroscience, UCSD
%                                2010-04-22
dp;

% parse arguments
opts = arg_define(0:1,varargin, ...
    ... % training data
    arg_norep({'data','Data'},[],[],'Data that can be partitioned using index sets. Such as, for example, {X,y} with X being the [NxF] training data and y being the [Nx1] labels (N=#trials, F=#features).'), ...
    ... % method to use and its arguments
    arg({'args','TrainingArguments'},{},[],'Arguments to the training function. Packed in a cell array. Search ranges are defined according to ArgumentFormat; see also utl_gridsearch for additional clarification on this. Note: if using the default trainer/tester functions, args must at least specify the learning function to be used, optionally followed by arguments to that learning function, e.g. {''lda''} for linear discriminant analysis or {''logreg'',''lambda'',0.1} for logistic regression with ''lambda'',0.1 passed in as user parameters (see ml_train* functions for options).','type','expression'), ...
    arg({'trainer','TrainingFunction'},'@ml_train',[],'Training function. Receives a partition of the data (as produced by the specified partitioner), possibly some further arguments as specified in args, and returns a model (of any kind).','type','expression'), ...
    arg({'tester','PredictionFunction'},'@utl_default_predict',[],'Prediction function. Receives a partition of the data (as produced by the partitioner) and a model (as produced by the trainer), and returns a prediction for every index of the input data, in one of the output formats permitted by ml_predict.','type','expression'), ...
    ... % nested cross-validation parameters
    arg({'scheme','EvaluationScheme'},10,[],'Cross-validation scheme. Defines what parts of the data shall be used for training or testing in each fold. Supports many different formats, see documentation.','type','expression'), ...
    arg({'metric','EvaluationMetric'},'auto',{'auto','mcr','auc','mse','medse','smse','mae','medae','smae','smedae','max','sign','rms','bias','kld','nll','cond_entropy','cross_entropy','f_measure'},'Evaluation metric. Loss measure employed to measure the quality of predictions on a test partition given its known target values; applied both to results in each fold and results aggregated over all folds. Can be either a predefined metric supported by ml_calcloss, or a custom function handle which receives an array of target values in the first argument and an array of predictions in the second argument; each can be in any format that can be produced by ml_predict (but can be expected to be mutually consistent). Shall return a real number indicating the summary metric over all data, and optionally additional statistics in a second output struct.','typecheck',false), ...
    arg({'repeatable','RepeatableResults'},1,[],'Produce repeatable (vs. randomized) results. If nonzero, the value is taken as the random seed to use for the performed calculation. This way, different numbers (aside from 0) give different repeatable runs.'), ...
    ... % support for custom data representations
    arg({'target','TargetFunction'},'@utl_default_target',[],'Target extraction function. A function to derive the target variable from a partition of the data (as produced by the partitioner), for evaluation; the allowed format is anything that may be output by ml_predict.','type','expression'), ...
    arg({'partitioner','PartitioningFunction'},'@utl_default_partitioner',[],'Partitioning function. See documentation for the function contract.','type','expression'), ...
    ... % parallel computing settings
    arg({'engine_gs','ParallelEngineGS'},'global',{'global','local','BLS','Reference','ParallelComputingToolbox'}, 'Parallel engine for grid search. This can either be one of the supported parallel engines (BLS for BCILAB Scheduler, Reference for a local reference implementation, and ParallelComputingToolbox for a PCT-based implementation), or local to skip parallelization altogether, or global to select the currently globally selected setting (in the global tracking variable).'), ...
    arg({'engine_ncv','ParallelEngineNCV'},'global',{'global','local','BLS','Reference','ParallelComputingToolbox'}, 'Parallel engine for cross-validation. This can either be one of the supported parallel engines (BLS for BCILAB Scheduler, Reference for a local reference implementation, and ParallelComputingToolbox for a PCT-based implementation), or local to skip parallelization altogether, or global to select the currently globally selected setting (in the global tracking variable).'), ...
    arg({'pool','WorkerPool'},'global',[], 'Worker pool to use. This is typically a cell array, but can also be the string ''gobal'', which stands for the currently globally set up worker pool (see global tracking variable).','type','expression'), ...
    arg({'policy','ReschedulingPolicy'},'global',[], 'Rescheduling policy. This is the name of the rescheduling policy function that controls if and when tasks are being rescheduled. If set to global, the current global setting will be used.'), ...
    ... % misc arguments
    arg({'argform','ArgumentFormat'},'clauses',{'clauses','direct'},'Argument search range format. If set to clauses, search ranges for individual arguments can be given using the search() expression; if set to direct, a cell array is expected each of whose elements is a cell array that represents a particular parameter set to try for the training function.'), ...
    arg({'forcestats','ForceStatistics'},0,[],'Enforce a cross-validation to obtain stats. Even when no search is required.'), ...
    arg({'cache_fold_results','CacheFoldResults'},false,[],'Whether to cache the per-fold results. This is meant to be used when running very long-running computations on machines that crash frequently enough that partial results need to be saved. In this case, any previously computed results will be loaded from disk.'), ...
    arg({'only_cached_results','OnlyCachedResults'},false,[],'Load only results that are in the cache. This will not run any computations (aside from pre-checks, that can be disabled by setting NoPrechecks to true).'), ...
    arg({'no_prechecks','NoPrechecks'},false,[],'Skip pre-checks that access the data. This can save some time when it would take very long to load the data, especially when performing parallel computation.'), ...
    arg({'tolerate_exceptions','TolerateExceptions'},false,[],'Tolerate exceptions during training. If this happens, folds where the training function yielded errors will be skipped.'), ...
    arg({'collect_models','CollectModels'},false,[],'Collect models per fold. Note that this increases the amount of data returned.'));

% if there are parameters to be searched...
if opts.forcestats || is_needing_search(opts.argform,opts.args)

    % the nested cross-validation (utl_crossval) receives a specific subset of our arguments
    nestedcv_opts = hlp_struct2varargin(opts,'suppress',{'engine_gs','forcestats','args','argform'},'rewrite',{'engine_ncv','engine_cv'});

    % the objective function of our search is utl_crossval with the above-defined options; plus, it
    % has free arguments that are passed into the cross-validation as the 'args' parameter (i.e.,
    % the arguments that it passes into its 'trainer' function)
    objfun = @(varargin) utl_crossval(nestedcv_opts{:}, 'args',varargin);

    % the grid search receives the above-defined objective function and a subset of our arguments
    search_opts = {'argform',opts.argform, 'engine_gs',opts.engine_gs, 'policy',opts.policy, ...
        'pool',opts.pool, 'func',objfun};

    % now execute the grid search over every argument combination specified in opts.args
    [stats.bestidx,stats.inputs,stats.outputs] = utl_gridsearch(search_opts, opts.args{:});

else

    % otherwise produce dummy stats if neither necessary nor forced
    stats = struct('bestidx',1, 'inputs',{{opts.args}}, 'outputs',{{NaN,struct()}});

end

% finally, using the best args, send the input data through the training function & compute a model
bestargs = stats.inputs{stats.bestidx};
model = opts.trainer(opts.data,bestargs{:});


function [medRV,minRV,medRVSS,minRVSS]=realized_min_med_variance(price,time,timeType,samplingType,samplingInterval,subsamples)
% Estimates truncated realized variance using the median and minimum method of Andersen, Dobrev and Schaumburg
%
% USAGE:
%   [MEDRV,MINRV] = realized_medrv(PRICE,TIME,TIMETYPE,SAMPLINGTYPE,SAMPLINGINTERVAL,SAMPLESPERBIN)
%   [MEDRV,MINRV,MEDRVSS,MINRVSS] = realized_medrv(PRICE,TIME,TIMETYPE,SAMPLINGTYPE,SAMPLINGINTERVAL,SAMPLESPERBIN,SUBSAMPLES)
%
% INPUTS:
%   PRICE            - m by 1 vector of high frequency prices
%   TIME             - m by 1 vector of times where TIME(i) corresponds to PRICE(i)
%   TIMETYPE         - String describing the way times are measured
%                       'wall'    24-hour clock of the form HHMMSS, e.g. 101543 or 153217
%                       'seconds' Time measured in seconds past midnight on the first day.
%                       'unit'  Unit normalized date format, e.g. .1, .234, .9
%                         Unit normalized times are more general than the other types and can be
%                         applied to data from more than one calendar day
%   SAMPLINGTYPE     - String describing the type of sampling to use when
%                        filtering PRICE
%                        'CalendarTime' - Sample in calendar time using observations separated by
%                          SAMPLINGINTERVAL seconds
%                        'CalendarUniform' - Sample in calendar time using SAMPLINGINTERVAL
%                          observations spread uniformly between TIME(1) and TIME(m)
%                        'BusinessTime' - Sample in business (tick) time using observation separated
%                          by SAMPLINGINTERVAL ticks
%                        'BusinessUniform' - Sample in business (tick) time using observations
%                          uniformly spaced in business time.
%                        'Fixed' - Sample at specific points in time. When using fixed,
%                          SAMPLINGINTERVAL must be a n by 1 vector of times with the same TIMETYPE
%                          as TIME (i.e. seconds if TIME is in seconds)
%   SAMPLINGINTERVAL  - Scalar integer or n by 1 vector whose meaning depends on SAMPLINGTYPE
%   SUBSAMPLES        - [OPTIONAL] Scalar integer indicating the number of subsample realized
%                         variance estimators to average with the original realized variance.
%                         Subsample realized variances are based on prices uniformly spaced between
%                         the times (Calendar sampling) or ticks (Business sampling).  SUBSAMPLES should
%                         be greater than 1, since 1 uses a single subsample and so is equivalent
%                         to the original RV will compute a subsample realized variance using the
%                         mid-point of the price sample points, 2 will use the original sample and
%                         the midpoint between the original sampling times, and so on.
%                         In general this number should be small so the subsample estimators will be "sparse".
%
% OUTPUTS:
%   MEDRV             - Median RV estimate
%   MINRV             - Minimum RV estimate
%   MEDRVSS           - Subsampled version of median RV estimate
%   MINRVSS           - Subsampled version of minimum RV estimate
%
% COMMENTS:
%
% EXAMPLE:
%  % Using all 1-minute returns between 9:30:00 and 16:00:00
%  fixedInterval = seconds2wall(wall2seconds(93000):60:wall2seconds(160000));
%  [MEDRV,MINRV] = realized_medrv(PRICE,TIME,'wall','Fixed',fixedInterval)
%
%  % Using all 5-minute returns between 9:30:00 and 16:00:00, but subsampling every minute
%  fixedInterval = seconds2wall(wall2seconds(93000):300:wall2seconds(160000));
%  [MEDRV,MINRV,MEDRVSS,MINRVSS] = realized_medrv(PRICE,TIME,'wall','Fixed',fixedInterval,5)
%
%  See also REALIZED_BIPOWER_VARIATION, REALIZED_KERNEL, REALIZED_QUANTILE_VARIANCE,
%  REALIZED_RANGE, REALIZED_PRICE_FILTER

% Copyright: Kevin Sheppard
% kevin.sheppard@economics.ox.ac.uk
% Revision: 1    Date: 10/24/2011

%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Input Checking
%%%%%%%%%%%%%%%%%%%%%%%%%%%%
if nargin<5 || nargin>6
    error('Five or Six inputs required.')
end
if size(price,2)>size(price,1)
    price=price';
end
if size(price,2)>1
    error('PRICE must be a m by 1 vector.')
end
if size(time,2)>size(time,1)
    time=time';
end
if any(diff(time)<0)
    error('TIME must be sorted and increasing')
end
if size(time,2)>1 || length(time)~=length(price)
    error('TIME must be a m by 1 vector.')
end
% Inserted to protect against inputing integer times
time = double(time);

timeType=lower(timeType);
if ~ismember(timeType,{'wall','seconds','unit'})
    error('TIMETYPE must be one of ''wall'', ''seconds'' or ''unit''.');
end
samplingType=lower(samplingType);
if ~ismember(samplingType,{'calendartime','calendaruniform','businesstime','businessuniform','fixed'})
    error('SAMPLINGTYPE must be one of ''CalendarTime'', ''CalendarUniform'', ''BusinessTime'', ''BusinessUniform'' or ''Fixed''.');
end

m=size(price,1);
t0=time(1);
tT=time(m);
if ismember(samplingType,{'calendartime','calendaruniform','businesstime','businessuniform'})
    % Must be a scalar integer if timeType is seconds or wall
    if ismember(timeType,{'wall','seconds'})
        if ~isscalar(samplingInterval) || floor(samplingInterval)~=samplingInterval || samplingInterval<1
            error('SAMPLINGINTERVAL must be a positive integer for the SAMPLINGTYPE selected when using ''wall'' or ''seconds'' as TIMETYPE.')
        end
    else
        if ~isscalar(samplingInterval) || samplingInterval<0
            error('SAMPLINGINTERVAL must be a positive value for the SAMPLINGTYPE selected when using ''unit'' as TIMETYPE.')
        end
    end
else
    if size(samplingInterval,2)>size(samplingInterval,1)
        samplingInterval=samplingInterval';
    end
    if ~(any(samplingInterval>=t0) && any(samplingInterval<=tT))
        error('At least one sampling interval must be between min(TIME) and max(TIME) when using ''Fixed'' as SAMPLINGTYPE.')
    end
    if any(diff(samplingInterval)<=0)
        error('When using ''Fixed'' as SAMPLINGTYPE the vector of sampling times in SAMPLINGINTERVAL must be sorted and strictly increasing.')
    end
end

if nargin<6 || isempty(subsamples)
    subsamples = 1;
end

if nargin==6 && ~isempty(subsamples)
    if ~isscalar(subsamples) || subsamples<0 || floor(subsamples)~=subsamples
        error('SUBSAMPLES must be a non-negative scalar.')
    end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Input Checking
%%%%%%%%%%%%%%%%%%%%%%%%%%%%

logPrice = log(price);
% Convert times to unit if not already unit
if ~strcmp(timeType,'unit')
    [time,~,~,samplingInterval]=realized_convert2unit(time,timeType,samplingType,samplingInterval);
end
% Since everything is converted to unit, set timeType to unit
timeType='unit';
% m is the length of price

% Filter prices and compute the RV
filteredLogPrice = realized_price_filter(logPrice,time,timeType,samplingType,samplingInterval);
% Construct the aggregated return series.  Remember will need to skip
returns = diff(filteredLogPrice);
m = length(returns);

returns2 = (sqrt(m)*returns).^2;
r2 = [returns2(1:m-2) returns2(2:m-1) returns2(3:m)];
medRV = mean(median(r2,2));
medRVScale = pi/(6-4*sqrt(3)+pi);
medRV = medRV*medRVScale ;

r2 = [returns2(1:m-1) returns2(2:m)];
minRV = mean(min(r2,[],2));
minRVscale = pi/(pi-2);
minRV = minRV*minRVscale;

subsampledLogPrices = realized_subsample(logPrice,time,timeType,samplingType,samplingInterval,subsamples);
medRVs = nan(m*subsamples,1);
minRVs = nan(m*subsamples,1);
medRVcount = 0;
minRVcount = 0;
for i=1:subsamples
    filteredLogPrice = subsampledLogPrices{i};
    returns = diff(filteredLogPrice);
    m = size(returns,1);
    returns2 = m*returns.^2;
    % MedRV
    r2 = [returns2(1:m-2) returns2(2:m-1) returns2(3:m)];
    n = size(r2,1);
    medRVs(medRVcount+(1:n)) = median(r2,2);
    medRVcount = medRVcount  + n;
    % MinRV
    r2 = [returns2(1:m-1) returns2(2:m)];
    n = size(r2,1);
    minRVs(minRVcount+(1:n)) = min(r2,[],2);
    minRVcount = minRVcount + n;
end
medRVSS = mean(medRVs(1:medRVcount));
medRVSS = medRVSS * medRVScale;
minRVSS = mean(minRVs(1:minRVcount));
minRVSS = minRVSS * minRVscale;


function Y = squeeze(X)
%SQUEEZE Remove singleton dimensions from a tensor.
%
%   Y = SQUEEZE(X) returns a tensor Y with the same elements as
%   X but with all the singleton dimensions removed.  A singleton
%   is a dimension such that size(X,dim)==1.
%
%   If X has *only* singleton dimensions, then Y is a scalar.
%
%   Examples
%   squeeze( tenrand([2,1,3]) ) %<-- returns a 2-by-3 tensor
%   squeeze( tenrand([1 1]) ) %<-- returns a scalar
%
%   See also TENSOR, TENSOR/RESHAPE, TENSOR/PERMUTE.
%
%MATLAB Tensor Toolbox.
%Copyright 2015, Sandia Corporation.

% This is the MATLAB Tensor Toolbox by T. Kolda, B. Bader, and others.
% http://www.sandia.gov/~tgkolda/TensorToolbox.
% Copyright (2015) Sandia Corporation. Under the terms of Contract
% DE-AC04-94AL85000, there is a non-exclusive license for use of this
% work by or on behalf of the U.S. Government. Export of this data may
% require a license from the United States Government.
% The full license terms can be found in the file LICENSE.txt

if all(X.size > 1)
  % No singleton dimensions to squeeze
  Y = X;
else
  idx = find(X.size > 1);
  if numel(idx) == 0
    % Scalar case - only singleton dimensions
    Y = X.data;
  else
    siz = X.size(idx);
    Y = tensor(squeeze(X.data),siz);
  end
end

return;


classdef FCP1 < PROBLEM
% <multi> <real> <constrained>
% Benchmark constrained MOP proposed by Jiawei Yuan

%------------------------------- Reference --------------------------------
% J. Yuan, H. Liu, Y. Ong, and Z. He, Indicator-based evolutionary
% algorithm for solving constrained multi-objective optimization problems,
% IEEE Transactions on Evolutionary Computation, 2022, 26(2): 379-391.
%------------------------------- Copyright --------------------------------
% Copyright (c) 2023 BIMK Group. You are free to use the PlatEMO for
% research purposes. All publications which use this platform or any code
% in the platform should acknowledge the use of "PlatEMO" and reference "Ye
% Tian, Ran Cheng, Xingyi Zhang, and Yaochu Jin, PlatEMO: A MATLAB platform
% for evolutionary multi-objective optimization [educational forum], IEEE
% Computational Intelligence Magazine, 2017, 12(4): 73-87".
%--------------------------------------------------------------------------

% This function is written by Jiawei Yuan

    methods
        %% Initialization
        function Setting(obj)
            obj.M = 2;
            if isempty(obj.D)
                obj.D = 30;
            end
            obj.lower    = zeros(1,obj.D);
            obj.upper    = ones(1,obj.D);
            obj.encoding = ones(1,obj.D);
        end
        %% Calculate objective values
        function PopObj = CalObj(obj,PopDec)
            g = 1 + 9*mean(PopDec(:,2:end),2);
            PopObj(:,1) = PopDec(:,1).*g;
            PopObj(:,2) = (1-PopDec(:,1)).*g;
        end
        %% Calculate constraint violations
        function PopCon = CalCon(obj,PopDec)
            g = 1 + 9*mean(PopDec(:,2:end),2);
            Dis    = abs(9-g);
            %%%%% Type-I constraints
            y1     = Dis.^2-0.25;
            y2     = 1./(Dis+1e-6).*(1.2+sin(Dis*pi));
            PopCon = min([y1,y2],[],2);
        end
        %% Sample reference points on Pareto front
        function P = GetOptimum(obj,N)
            P=8.5*UniformPoint(N,obj.M);
        end
    end
end


function z=convfft(x,h,d,m,h0,x1,x2)
%CONFFT 1-D convolution or correlation using FFT
%
%  Usage: (1) z=convfft(x,h,d1,'',1,1,size(x,d)+length(h)-1);            % equivalent to conv(x,h)
%
%         (2) hh=convfft(length(x),h,2,'z',1,1,length(x)+length(h)-1);   % precalculate fft(h) for multiple calls
%             z=convfft(x,hh);                                           % also equivalent to conv(x,h)
%
%         (3) z=convfft(x,h);                                            % equivalent to filter(h,1,x)
%             z=convfft(x,h,d1,'',1,1,size(x,d));                        % equivalent to filter(h,1,x)
%
%         (4) z=convfft(x,h,d,'X',1,2-max(size(x,d),length(h)),max(size(x,d),length(h)));  % equivalent to xcorr(x,h)
%
%         (5) z=convfft(x,h,d,'x',floor((1+length(h))/2),1,size(x,d));   % equivalent to imfilter(x,h)
%
%  Inputs: x     input vector or array (or size(x,d) for the 'z' option)
%          h     impulse response (or z output from previous call with the 'z' option)
%          d     dimension of x to do convolution along [first non-singleton]
%          m     mode options (see below)
%          h0    origin sample number in h (can be outside the range 1:length(h)) [default: 1]
%          x1,x2 range of rows/columns in x to align with origin of h (can be outside the range 1:size(x,d)) [default: 1,size(x,d)]
%
% Outputs: z     output from convolution/correlation. The same size and shape as x except that size(z,d)=x2-x1+1
%                If m='z' is specified, then z is a structure that can be used as h in a subsequent call
%
% Mode is any sensible combination of the following
%          'x'   perform real correlation rather than convolution (reflects h around sample h0)
%          'X'   perform complex correlation rather than convolution (reflects and conjugates h around sample h0)
%          'z'   Precalculate fft(h) for efficiency. Input d must be given explicitly and input x equals size(x,d).

%      Copyright (C) Mike Brookes 2016-2017
%      Version: $Id: convfft.m 10118 2017-09-17 19:45:51Z dmb $
%
%   VOICEBOX is a MATLAB toolbox for speech processing.
%   Home page: http://www.ee.ic.ac.uk/hp/staff/dmb/voicebox/voicebox.html
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%   This program is free software; you can redistribute it and/or modify
%   it under the terms of the GNU General Public License as published by
%   the Free Software Foundation; either version 2 of the License, or
%   (at your option) any later version.
%
%   This program is distributed in the hope that it will be useful,
%   but WITHOUT ANY WARRANTY; without even the implied warranty of
%   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%   GNU General Public License for more details.
%
%   You can obtain a copy of the GNU General Public License from
%   http://www.gnu.org/copyleft/gpl.html or by writing to
%   Free Software Foundation, Inc.,675 Mass Ave, Cambridge, MA 02139, USA.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
if ~isstruct(h) % normal input calling sequence
    if nargin<4 || isempty(m)
        m='';                       % set default mode string
    end
    if nargin<5 || isempty(h0)
        h0=1;                       % set default h origin
    end
    s=size(x);                      % get the structure of x
    ps=numel(x);                  	% number of elements in x
    ns=length(s);                   % number of dimensions in x
    if any(m=='z')      % output pre-computed structure instead of convolution
        if nargin<3 || isempty(d)   % if d unspecified
            error('d must be specified explicitly');
        end
        if numel(x)~=1              % x contains nx for a future call
            error('x must equal size(*,d)');
        end
        nx=x;                       % save x as the value of nx
    else
        if nargin<3 || isempty(d)       % if d unspecified
            if ps<2
                d=1;                    % if d is a singleton or is empty
            else
                d=find(s>1,1);          % d = first nonsingleton dimension
            end
        end
        nx=s(d);                        % length in correlation dimension
    end
    k=ps/nx;                        % total size of all other dimensions
    if nargin<6 || isempty(x1)
        x1=1;                       % default initial lag
    end
    if nargin<7 || isempty(x2)
        x2=nx;                      % default final lag
    end
    nz=x2-x1+1;                     % number of output lags required
    h=h(:);                         % force h to be a column
    nh=length(h);
    if any(m=='X')                  % do complex correlation rather than convolution
        h=conj(h(nh:-1:1));         % reflect and conjugate h
        h0=nh+1-h0;                 % reflect the position of h0
    elseif any(m=='x')              % do real correlation
        h=h(nh:-1:1);               % reflect h
        h0=nh+1-h0;                 % reflect the position of h0
    end
    hmin=h0+x1-nx;                  % smallest h index ever used
    hmax=h0+x2-1;                   % largest h index ever used
    xmin=x1+h0-nh;                  % smallest x index ever used
    xmax=x2+h0-1;                   % largest x index ever used
    if hmin>1 || hmax<nh            % we can delete some unused h values
        hmin=max(hmin,1);
        h=h(hmin:min(hmax,nh));     % trim h if possible
        nh=length(h);
        h0=h0-hmin+1;               % update h0 to new position
    end
    if xmin>1 || xmax<nx            % we can delete some unused x values
        vmin=max(xmin,1);           % we will trim v to v(vmin:vmax)
        vmax=min(xmax,nx);
        x1=x1-vmin+h0;              % update x1,x2 to new positions assuming h0=0
        x2=x2-vmin+h0;
    else
        vmin=1;
        vmax=nx;
        x1=x1+h0-1;                 % update x1,x2 to new positions assuming h0=0
        x2=x2+h0-1;
    end
    nv=vmax-vmin+1;                 % number of elements of v to retain
    nxz=min(max(max(nh-x1,0),max(x2-nv,0)),nh-1); % number of zeros to add to v is the larger of the number needed at each end
    [fnx,enx]=log2(nv+nxz);         % round up length of zero-padded v to next power of 2
    nf=pow2(1,enx-(fnx==0.5));      % actual length of dft is a power of 2
    fmin=max(x1,1);                 % range of indices to extract from circular convolution
    fmax=min(x2,min(nf,nx+nh-1));
    zmin=max(1,2-x1);               % range of indices for non-zero output values
    zmax=zmin+fmax-fmin;
    fh=fft(h,nf,1);
else                % h is the z output of a previous call with the 'z' option
    d=h.d;          % x dimension to convolve along
    nx=h.nx;        % original size(x,d)
    ns=h.ns;        % number of dimensions of x
    nv=h.nv;        % number x values needed (might be < nx)
    vmin=h.vmin;    % first x value needed (might be > 1)
    vmax=h.vmax;    % last x value needed (might be < nx)
    nf=h.nf;        % dft length
    fh=h.fh;        % dft of impulse response h
    fmin=h.fmin;    % first fh value needed
    fmax=h.fmax;    % last fh value needed
    nz=h.nz;        % output size(z,d)
    zmin=h.zmin;    % first non-zero z value
    zmax=h.zmax;    % last non-zero z value
    s=size(x);      % get the structure of x
    k=numel(x)/nx;  % total size of the rest of x
    if size(x,d)~=nx || length(s)~=ns
        error('input x has incompatible dimensions');
    end
end
if  ~isstruct(h) && any(m=='z')    % save required information as a structure for next call
    z.d=d;          % x dimension to convolve along
    z.nx=nx;        % original size(x,d)
    z.ns=ns;        % number of dimensions of x
    z.nv=nv;        % number x values needed (might be < nx)
    z.vmin=vmin;    % first x value needed (might be > 1)
    z.vmax=vmax;    % last x value needed (might be < nx)
    z.nf=nf;        % dft length
    z.fh=fh;        % dft of impulse response h
    z.fmin=fmin;    % first fh value needed
    z.fmax=fmax;    % last fh value needed
    z.nz=nz;        % output size(z,d)
    z.zmin=zmin;    % first non-zero z value
    z.zmax=zmax;    % last non-zero z value
elseif ~isempty(x)  % there is data to convolve
    if d==1         % reshape x if necessary
        v=reshape(x,nx,k);
    else
        v=reshape(permute(x,[d:ns 1:d-1]),nx,k);
    end
    if nv<nx        % we can delete some unused v values
        v=v(vmin:vmax,:);
    end
    zz=ifft(fft(v,nf,1).*repmat(fh,1,k));    % do the convolution
    z=zeros(nz,k);                  % reserve space for the output
    z(zmin:zmax,:)=zz(fmin:fmax,:); % extract values from the convolution
    s(d)=nz;                        % update the size of the output
    if d==1
        z=reshape(z,s);
    else
        z=permute(reshape(z,s([d:ns 1:d-1])),[ns+2-d:ns 1:ns+1-d]);
    end
else
    z=[];           % no input data
end


%% IMAGE_BATCH_LOCAL uses the BATCH command to run the IMAGE code locally.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    01 April 2010
%
%  Author:
%
%    John Burkardt
%
  clear

  fprintf ( 1, '\n' );
  fprintf ( 1, 'IMAGE_BATCH_LOCAL\n' );
  fprintf ( 1, '  Run IMAGE_SCRIPT locally\n' );
%
%  Set the number of workers in the pool.
%
  n = 3;
%
%  BATCH sends the script for execution.
%
  job = batch ( 'image_script', ...
    'Configuration', 'local', ...
    'CaptureDiary', true, ...
    'FileDependencies', { 'image_fun', 'balloons.tif' }, ...
    'matlabpool', n );
%
%  WAIT pauses the MATLAB session til the job completes.
%
  wait ( job );
%
%  DIARY displays any messages printed during execution.
%
  diary ( job );
%
%  LOAD makes the script's workspace avaiable.
%
%  x = output #1.
%  y = output #2.
%  z = output #3.
%
  load ( job );
%
%  DESTROY cleans up data about the job we no longer need.
%
  destroy ( job );
%
%  Display the original, noisy, and filtered versions.
%
  figure;

  subplot ( 1, 3, 1 );
  imshow ( x );
  title ( 'Original image' );

  subplot ( 1, 3, 2 );
  imshow( y );
  title( 'Noisy Image' );

  subplot ( 1, 3, 3 );
  imshow ( z );
  title ( 'Median Filtered Image' );


function [] = mci_plot_dist_multi (dist,name,P)
% Plot (multiple) densities
% FORMAT [] = mci_plot_dist_multi (dist,name,P)
%
% dist{i}   ith distribution
%
% .Ep       mean
% .P        [Np x Ns] sample matrix
% .ind      indices of samples (eg. post burn-in)
% .names    names of variables
% .color    eg 'r','k','b'
% .order    eg. [1,3,4,2] to plot only variables 1,3,4 and 2 in that order
%
% name      name of parameters
% P         true parameters (optional)
%__________________________________________________________________________
% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging

% Will Penny
% $Id: mci_plot_dist_multi.m 6697 2016-01-27 14:57:28Z spm $

Ndist=length(dist);
for i=1:Ndist
    if strcmpi(dist{i}.type,'sample')
        figure;
        plot(dist{i}.P');
        xlabel('Sample');
        ylabel('Parameters');
        title(sprintf('%s trajectories',name));
    end
end

Np=length(dist{1}.Ep);
try
    order=dist{1}.order;
    Np=length(order);
catch
    order=[1:Np];
end

% Plot univariate densities
rNp=ceil(sqrt(Np));
h=figure;
set(h,'Name',name);
for j=1:Np,
    subplot(rNp,rNp,j);
    jp=order(j);
    for i=1:Ndist,
        mci_plot_dist(dist{i},jp);
        hold on
    end
    yl=get(gca,'YLim');
    if nargin > 2
        plot([P(jp),P(jp)],[0,yl(2)],'r','LineWidth',2);
    end
end
if nargin > 2
    disp('True parameters shown in red');
end

plot_bivariate=0;
if plot_bivariate
    % Plot bivariate densities
    rNp=ceil(sqrt(Np));
    h=figure;
    set(h,'Name',name);
    k=1;
    q=dist{1}.P(:,dist{1}.ind);
    for i=1:Np,
        for j=1:Np,
            if j > i
                subplot(Np,Np,k);
                plot(q(i,:),q(j,:),'k.');
                xlabel(dist{1}.names{i});
                ylabel(dist{1}.names{j});
                hold on
                plot(P(i),P(j),'ro');
            end
            k=k+1;
        end
    end
    if nargin > 2
        disp('True parameters shown in red');
    end

    % Posterior Correlation Matrix
    for i=1:Ndist,
        if strcmp(lower(dist{i}.type),'sample')
            disp(' ');
            disp(sprintf('Distribution %d',i));
            disp('Posterior Correlation Matrix:');
            corrcoef(dist{i}.P(:,dist{i}.ind)')
        end
    end
end


function c = triu(a,k)
%TRIU         Implements  triu(a,k)  for intervals
%
%   c = triu(a,k)
%
% functionality as Matlab function triu for matrices
%

% written  10/16/98     S.M. Rump
% modified 11/30/98     S.M. Rump  improved speed
% modified 04/04/04     S.M. Rump  set round to nearest for safety
% modified 04/06/05     S.M. Rump  rounding unchanged
%

  if nargin==1
    k = 0;
  end

  c = a;
  if a.complex
    c.mid = triu(a.mid,k);
    c.rad = triu(a.rad,k);
  else
    c.inf = triu(a.inf,k);
    c.sup = triu(a.sup,k);
  end


clear all; close all;
I=imread('circles.png');
J=bwulterode(I);
figure;
subplot(121);  imshow(I);
subplot(122);  imshow(J);


function hintonDiagram(w,max_m,min_m)
% Display a matrix W where the square's area represents W(i,j)
% and red = negative, green = positive
%
%  Examples
%
%    W = randn(4,5); hintonDiagram(W)

% This file is from pmtk3.googlecode.com

% Based on hintonw function from Mathworks neural net toolbox

if nargin < 1,error('Not enough input arguments.');end
if nargin < 2, max_m = max(max(abs(w))); end
if nargin < 3, min_m = max_m / 100; end
if max_m == min_m, max_m = 1; min_m = 0; end

% DEFINE BOX EDGES
xn1 = [-1 -1 +1]*0.5;
xn2 = [+1 +1 -1]*0.5;
yn1 = [+1 -1 -1]*0.5;
yn2 = [-1 +1 +1]*0.5;

% DEFINE POSITIVE BOX
xn = [-1 -1 +1 +1 -1]*0.5;
yn = [-1 +1 +1 -1 -1]*0.5;

% DEFINE POSITIVE BOX
xp = [xn [-1 +1 +1 +0 +0]*0.5];
yp = [yn [+0 +0 +1 +1 -1]*0.5];

[S,R] = size(w);

cla reset
hold on
set(gca,'xlim',[0 R]+0.5);
set(gca,'ylim',[0 S]+0.5);
set(gca,'xlimmode','manual');
set(gca,'ylimmode','manual');
xticks = get(gca,'xtick');
set(gca,'xtick',xticks(find(xticks == floor(xticks))))
yticks = get(gca,'ytick');
set(gca,'ytick',yticks(find(yticks == floor(yticks))))
set(gca,'ydir','reverse');
if get(0,'screendepth') > 1
  %set(gca,'color',[1 1 1]*.5);
  %set(gcf,'color',[1 1 1]*.3);
end

for i=1:S
  for j=1:R
    m = sqrt((abs(w(i,j))-min_m)/max_m);
    m = min(m,max_m)*0.95;
    if real(m)
      if w(i,j) >= 0
        fill(xn*m+j,yn*m+i,[0 0.8 0])
        plot(xn1*m+j,yn1*m+i,'w',xn2*m+j,yn2*m+i,'k')
      elseif w(i,j) < 0
        fill(xn*m+j,yn*m+i,[0.8 0 0]);
        plot(xn1*m+j,yn1*m+i,'k',xn2*m+j,yn2*m+i,'w');
      end
    end
  end
end

plot([0 R R 0 0]+0.5,[0 0 S S 0]+0.5,'w');
%xlabel('Input');
%ylabel('Neuron');
grid on

end


function cy = zaxpy ( n, ca, cx, incx, cy, incy )

%*****************************************************************************80
%
%% ZAXPY computes a complex constant times a vector plus a vector.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    12 April 2009
%
%  Author:
%
%    MATLAB version by John Burkardt.
%
%  Reference:
%
%    Jack Dongarra, Cleve Moler, Jim Bunch, Pete Stewart,
%    LINPACK User's Guide,
%    SIAM, 1979.
%
%    Charles Lawson, Richard Hanson, David Kincaid, Fred Krogh,
%    Basic Linear Algebra Subprograms for Fortran Usage,
%    Algorithm 539,
%    ACM Transactions on Mathematical Software,
%    Volume 5, Number 3, September 1979, pages 308-323.
%
%  Parameters:
%
%    Input, integer N, the number of elements in CX and CY.
%
%    Input, complex CA, the multiplier of CX.
%
%    Input, complex CX(*), the first vector.
%
%    Input, integer INCX, the increment between successive entries of CX.
%
%    Input, complex CY(*), the second vector.
%
%    Input, integer INCY, the increment between successive entries of CY.
%
%    Output, complex CY(*), the value of CY(*) + CA * CX(*).
%
  cy(1:incy:1+(n-1)*incy) = cy(1:incy:1+(n-1)*incy) ...
    + ca * cx(1:incx:1+(n-1)*incx);

  return
end


function f = not(f)
%~   CHEBFUN logical NOT.
%   NOT(F) returns a CHEBFUN which evaluates to zero at all points where F is
%   nonzero and one otherwise.

% Copyright 2017 by The University of Oxford and The Chebfun Developers.
% See http://www.chebfun.org/ for Chebfun information.

% Add breaks at the roots (since these will take the value 1 in the output).
f = addBreaksAtRoots(f);

% Tolerance:
vs = vscale(f);
vs(vs < eps) = 1;
tol = eps*vs;

% Loop over the FUNs:
for k = 1:numel(f.funs)
    f.funs{k} = not(f.funs{k});
end

% pointValues:
f.pointValues = abs(f.pointValues) < tol;

end


function Mh = hmxCopy(Mh,tol)
%+========================================================================+
%|                                                                        |
%|         OPENHMX - LIBRARY FOR H-MATRIX COMPRESSION AND ALGEBRA         |
%|           openHmx is part of the GYPSILAB toolbox for Matlab           |
%|                                                                        |
%| COPYRIGHT : Matthieu Aussal (c) 2017-2018.                             |
%| PROPERTY  : Centre de Mathematiques Appliquees, Ecole polytechnique,   |
%| route de Saclay, 91128 Palaiseau, France. All rights reserved.         |
%| LICENCE   : This program is free software, distributed in the hope that|
%| it will be useful, but WITHOUT ANY WARRANTY. Natively, you can use,    |
%| redistribute and/or modify it under the terms of the GNU General Public|
%| License, as published by the Free Software Foundation (version 3 or    |
%| later,  http://www.gnu.org/licenses). For private use, dual licencing  |
%| is available, please contact us to activate a "pay for remove" option. |
%| CONTACT   : matthieu.aussal@polytechnique.edu                          |
%| WEBSITE   : www.cmap.polytechnique.fr/~aussal/gypsilab                 |
%|                                                                        |
%| Please acknowledge the gypsilab toolbox in programs or publications in |
%| which you use it.                                                      |
%|________________________________________________________________________|
%|   '&`   |                                                              |
%|    #    |   FILE       : hmxCopy.m                                     |
%|    #    |   VERSION    : 0.40                                          |
%|   _#_   |   AUTHOR(S)  : Matthieu Aussal                               |
%|  ( # )  |   CREATION   : 14.03.2017                                    |
%|  / 0 \  |   LAST MODIF : 14.03.2018                                    |
%| ( === ) |   SYNOPSIS   : Copy and recompreesion of H-Matrix with new   |
%|  `---'  |                accuracy                                      |
%+========================================================================+

%%% H-Matrix (recursion)
if (Mh.typ == 0)
    for i = 1:4
        Mh.chd{i} = hmxCopy(Mh.chd{i},tol);
        Mh.tol    = max(tol,Mh.tol);
    end
    Mh = hmxFusion(Mh);

%%% Compressed leaf
elseif (Mh.typ == 1)
    if (tol > Mh.tol)
        [A,B]  = hmxQRSVD(Mh.dat{1},Mh.dat{2},tol);
        Mh.dat = {A,B};
        Mh.tol = tol;
    end

%%% Full leaf
elseif (Mh.typ == 2)

%%% Unknown type
else
    error('hmxCopy.m : unavailable case')
end
end


function [ ap, det, inert ] = chpdi ( ap, n, ipvt, job )

%*****************************************************************************80
%
%% CHPDI: determinant, inertia and inverse of a complex hermitian matrix.
%
%  Discussion:
%
%    The routine uses the factors from CHPFA.
%
%    The matrix is stored in packed form.
%
%    A division by zero will occur if the inverse is requested and CHPCO has
%    set RCOND == 0.0 or CHPFA has set INFO ~= 0.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    13 May 2007
%
%  Author:
%
%    MATLAB version by John Burkardt
%
%  Reference:
%
%    Jack Dongarra, Jim Bunch, Cleve Moler, Pete Stewart,
%    LINPACK User's Guide,
%    SIAM, 1979,
%    ISBN13: 978-0-898711-72-1,
%    LC: QA214.L56.
%
%  Parameters:
%
%    Input, complex AP(N*(N+1)/2); the factored matrix
%    from CHPFA.
%
%    Input, integer N, the order of the matrix.
%
%    Input, integer IPVT(N), the pivot vector from CHPFA.
%
%    Input, integer JOB, has the decimal expansion ABC where:
%    if C ~= 0, the inverse is computed,
%    if B ~= 0, the determinant is computed,
%    if A ~= 0, the inertia is computed.
%    For example, JOB = 111 gives all three.
%
%    Output, complex AP(N*(N+1)/2); if the inverse was requested, then
%    the upper triangle of the inverse of the original matrix, stored in packed
%    form.  The columns of the upper triangle are stored sequentially in a
%    one-dimensional array.
%
%    Output, real DET(2), if requested, the determinant of the original matrix.
%    Determinant = DET(1) * 10.0**DET(2) with 1.0 <= abs ( DET(1) ) < 10.0
%    or DET(1) = 0.0.
%
%    Output, integer INERT(3), if requested, the inertia of the original matrix.
%    INERT(1) = number of positive eigenvalues.
%    INERT(2) = number of negative eigenvalues.
%    INERT(3) = number of zero eigenvalues.
%
  det = [];
  inert = [];

  noinv = mod ( job, 10 ) == 0;
  nodet = floor ( mod ( job, 100 ) / 10 ) == 0;
  noert = floor ( mod ( job, 1000 ) / 100 ) == 0;

  if ( ~nodet | ~noert )

    if ( ~noert )
      inert(1:3) = 0;
    end

    if ( ~nodet )
      det(1) = 1.0;
      det(2) = 0.0;
    end

    t = 0.0;
    ik = 0;

    for k = 1 : n

      kk = ik + k;
      d = real ( ap(kk) );
%
%  Check if 1 by 1
%
      if ( ipvt(k) <= 0 )
%
%  2 by 2 block
%  Use DET (D  S; S  C)  =  ( D / T * C - T ) * T, T = abs ( S )
%  to avoid underflow/overflow troubles.
%  Take two passes through scaling.  Use T for flag.
%
        if ( t == 0.0 )
          ikp1 = ik + k;
          kkp1 = ikp1 + k;
          t = abs ( ap(kkp1) );
          d = ( d / t ) * real ( ap(kkp1+1) ) - t;
        else
          d = t;
          t = 0.0;
        end

      end

      if ( ~noert )

        if ( 0.0 < d )
          inert(1) = inert(1) + 1;
        elseif ( d < 0.0 )
          inert(2) = inert(2) + 1;
        elseif ( d == 0.0 )
          inert(3) = inert(3) + 1;
        end

      end

      if ( ~nodet )

        det(1) = det(1) * d;

        if ( det(1) ~= 0.0 )

          while ( abs ( det(1) ) < 1.0 )
            det(1) = det(1) * 10.0;
            det(2) = det(2) - 1.0;
          end

          while ( 10.0 <= abs ( det(1) ) )
            det(1) = det(1) / 10.0;
            det(2) = det(2) + 1.0;
          end

        end

      end

      ik = ik + k;

    end

  end
%
%  Compute inverse(A).
%
  if ( ~noinv )

    k = 1;
    ik = 0;

    while ( k <= n )

      km1 = k - 1;
      kk = ik + k;
      ikp1 = ik + k;
      kkp1 = ikp1 + k;
%
%  1 by 1
%
      if ( 0 <= ipvt(k) )

        ap(kk) = 1.0 / real ( ap(kk) );

        if ( 1 <= km1 )

          work(1:km1) = ap(ik+1:ik+km1);

          ij = 0;
          for j = 1 : km1
            jk = ik + j;
            ap(jk) = conj ( ap(ij+1:ij+j) ) * tranpose ( work(1:j) );
            ap(ik+1:ik+j-1) = ap(ik+1:ik+j-1) + work(j) * ap(ij+1:ij+j-1);
            ij = ij + j;
          end

          ap(kk) = ap(kk) + real ( conj ( work(1:km1) ) * transpose ( ap(ik+1:ik+km1) ) );

        end

        kstep = 1;
%
%  2 by 2
%
      else

        t = abs ( ap(kkp1) );
        ak = real ( ap(kk) ) / t;
        akp1 = real ( ap(kkp1+1) ) / t;
        akkp1 = ap(kkp1) / t;
        d = t * ( ak * akp1 - 1.0 );
        ap(kk) = akp1 / d;
        ap(kkp1+1) = ak / d;
        ap(kkp1) = -akkp1 / d;

        if ( 1 <= km1 )

          work(1:km1) = ap(ikp1+1:ikp1+km1);

          ij = 0;
          for j = 1 : km1
            jkp1 = ikp1 + j;
            ap(jkp1) = conj ( ap(ij+1:ij+j) ) * transpose ( work(1:j) );
            ap(ikp1+1:ikp1+j-1) = ap(ikp1+1:ikp1+j-1) ...
              + work(j) * ap(ij+1:ij+j-1);
            ij = ij + j;
          end

          ap(kkp1+1) = ap(kkp1+1) ...
            + real ( conj ( work(1:km1) ) * transpose ( ap(ikp1+1) ) );

          ap(kkp1) = ap(kkp1) ...
            + conj ( ap(ik+1:ik+km1) ) * transpose ( ap(ikp1+1:ikp1+km1) );

          work(1:km1) = ap(ik+1:ik+km1);

          ij = 0;

          for j = 1 : km1
            jk = ik + j;
            ap(jk) = conj ( ap(ij+1:ij+j) ) * transpose ( work(1:j) );
            ap(ik+1:ik+j-1) = ap(ik+1:ik+j-1) + work(j) * ap(ij+1:ij+j-1);
            ij = ij + j;
          end

          ap(kk) = ap(kk) ...
            + real ( conj ( work(1:km1) ) * transpose ( ap(ik+1:ik+km1) ) );

        end

        kstep = 2;

      end
%
%  Swap
%
      ks = abs ( ipvt(k) );

      if ( ks ~= k )

        iks = ( ks * ( ks - 1 ) ) / 2;

        temp             = ap(iks+1:iks+ks);
        ap(iks+1:iks+ks) = ap(ik+1:ik+ks);
        ap(ik+1:ik+ks)   = temp;

        ksj = ik + ks;

        for jb = ks : k

          j = k + ks - jb;
          jk = ik + j;

          temp    = conj ( ap(jk) );
          ap(jk)  = conj ( ap(ksj) );
          ap(ksj) = temp;

          ksj = ksj - ( j - 1 );

        end

        if ( kstep ~= 1 )

          kskp1 = ikp1 + ks;

          temp      = ap(kskp1);
          ap(kskp1) = ap(kkp1);
          ap(kkp1)  = temp;

        end

      end

      ik = ik + k;

      if ( kstep == 2 )
        ik = ik + k + 1;
      end

      k = k + kstep;

    end

  end

  return
end


function [ Accum_Grad ] = average_gradients_in_frames( Frames )
%AVERAGE_GRADIENTS_IN_FRAMES Summary of this function goes here
%   Detailed explanation goes here
n_frames=length(Frames);
Accum_Grad=[];
for layer=1:length(Frames{1})
    if isfield(Frames{1}(layer),'dzdw')&& ~isempty(Frames{1}(layer).dzdw)
        Accum_Grad(layer).dzdw=0;
        for f=1:n_frames
            Accum_Grad(layer).dzdw=Accum_Grad(layer).dzdw+Frames{f}(layer).dzdw;
        end
        Accum_Grad(layer).dzdw=Accum_Grad(layer).dzdw./n_frames;
    end

    if isfield(Frames{1}(layer),'dzdb')
        if ~isempty(Frames{1}(layer).dzdb)
            Accum_Grad(layer).dzdb=0;
            for f=1:n_frames
                Accum_Grad(layer).dzdb=Accum_Grad(layer).dzdb+Frames{f}(layer).dzdb;
            end
            Accum_Grad(layer).dzdb=Accum_Grad(layer).dzdb./n_frames;
        else
            Accum_Grad(layer).dzdb=[];
        end
    end
end

end


function [p,t,stats,terms,arg] = rmanova(dat,varargin)
% RMANOVA - performs a conventional n-way analysis of variance (ANOVA)
% or a repeated-measures ANOVA. A conventional ANOVA assumes a
% between-subjects design (different groups), a RM ANOVA a within-subjects
% design (all subjects participated in all conditions).
%
%Usage:
% [p,t,stats,terms,arg] = rmanova(dat,<OPT>)
% [p,t,stats,terms,arg] = rmanova(dat,varnames,<OPT>)
%
%Arguments:
% DAT      -  N*F1*F2*F3*...*FN data matrix.
%             First dimension (rows): refers to the subjects, ie each row
%             contains the data of one subject. The second and successive
%             dimensions contain the
%             factors, with the size of the dimension being the number of
%             levels of that factor. Example: for 13 subjects and the
%             factors Speller with 3 levels (Hex,Cake,Center speller) and
%             Attention with 2 levels (overt, covert), the data matrix
%             would have a size of 13*3*2. The entry dat(5,2,1) refers to
%             the fifth subject for Cake Speller (2nd level of Speller) and
%             overt attention (1st level of Attention).
%
% OPT - struct or property/value list of optional properties:
% 'Varnames'   - CELL ARRAY of one or more factors (eg {'Speller'
%              'Attention'}). The order of the factors must correspond
%              to the order of the factors in the DAT matrix.
% 'Design' -  Test design. 'independent' performs a conventional ANOVA.
%             If 'repeated-measures' (default), performs a
%             repeated-measures ANOVA. In the latter case, Subject is
%             included as a random (ie not fixed) effect.
% 'Display'  - if 'on' displays a table with the results (default 'on')
%
% Assumptions:
%   ANOVA   - homogeneity of variance: variances within each group are
%   equal
%   RM ANOVA - sphericity. Variation of population >difference< scores are
%   the same for all differences (for >2 levels of a factor).
%
% All other options are passed to the 'anovan' function.
%
%Returns:
% [p,t,stats,terms]     - 'help anovan' for details
% arg                   - arguments passed to anovan
%
% See also ANOVAN, PLOT_STATS.
%
% Note: If your experiment involves repeated-measures (your subject was run in all
% subconditions) you should use repeated-measures ANOVA (RM-ANOVA), because the
% assumption of independence of samples is violated. Furthermore, RM-ANOVA
% accounts for inter-subject variability and thus has more statistical
% power.

% Author(s): Matthias Treder 2011

varnames = [];
if nargin==2
  varnames = varargin{1};
  varargin={};
elseif  nargin>1 && iscell(varargin{1})
  varnames = varargin{1};
  varargin = varargin(2:end);
end

props = {'Varnames',             varnames,          'CELL{CHAR}|CHAR';
         'Design',          'independent',          '!CHAR(repeated-measures independent)';
         'Display',                'on',            '!CHAR(on off)';
         'Alpha'                    .05,            '!DOUBLE[1]';
         'Model'                    'full',         '!CHAR(linear interaction full)|DOUBLE';
         'SSType'                   3,              '!CHAR(h)|INT';
         'Table'                    1,              '!BOOL';
         'Verbose'                  1,              '!BOOL';
         };

if nargin==0,
  p= props; return
end

opt= opt_proplistToStruct(varargin{:});
[opt,isdefault] = opt_setDefaults(opt, props);
opt_checkProplist(opt, props);
misc_checkType(dat,'!DOUBLE');

if ischar(opt.Varnames)
    opt.Varnames = {opt.Varnames};
end

ss = size(dat);
nsbj = ss(1);           % Number of subjects
level = ss(2:end);

%% Factors and levels
% Number of factors
if ~isempty(opt.Varnames)
  nfac = numel(opt.Varnames);
  if nfac ~= ndims(dat)-1
    error('Number of factors %d does not match factors in data %d',nfac,ndims(dat)-1)
  end
else
  nfac = ndims(dat)-1;
end

% If no factor names provided, use default names
if isempty(opt.Varnames)
  vn = num2cell(1:nfac);
  vn = cellfun(@num2str,vn,'UniformOutput',0);
  opt.Varnames = strcat('X',vn);
end

%% Check (RM) ANOVA assumptions // TODO

if strcmp(opt.Design,'independent')
  % Homogenity of variance
  % TODO
elseif strcmp(opt.Design,'repeated-measures')
  % Sphericity
end

%% ANOVA or rmANOVA
if opt.Verbose
  names = cell2mat(strcat(opt.Varnames,',')); names=names(1:end-1);
  fprintf('Performing a %d-way %s ANOVA with factors {%s} and %s = %d levels.\n',...
    nfac,opt.Design,names,str_vec2str(level,'%d','x'),prod(level))
end

if strcmp(opt.Design,'independent')
  % ANOVA model
  design = orthogonal_design(nsbj,level);

elseif strcmp(opt.Design,'repeated-measures')
  % Repeated measures ANOVA
  opt.Varnames = {'Subject' opt.Varnames{:}};  % Add subject as a factor
  if isfield(opt,'random')   % Add subject as random effect
    opt.Random = [1 opt.Random+1];
  else
    opt.Random = 1;
  end
  design = orthogonal_design(1,[nsbj level]);
  % Specify to-be-tested interactions by hand to omit Subject
  model = double(flipud(dec2bin(0:2^(nfac+1)-1))-'0');
  model( model(:,1) & sum(model(:,2:end),2)>0 , :) = []; % Omit all interactions involving Subject
  maineffects = find(sum(model,2)==1);
  interactions = find(sum(model,2)>1);
  opt.Model = model([maineffects; interactions],:); % Bring in right order
else
  error('Unknown design ''%s''',opt.Design)
end

%% Perform ANOVA
arg = opt_structToProplist(rmfield(opt,{'Design','Table','Verbose','Alpha'}));

[p,t,stats,terms] = anovan(dat(:),design.anova,arg{:});

%% Provide output
if opt.Verbose
  fprintf('-------\nResults\n-------\n')
  % Find col indices
  F = find(ismember(t(1,:),'F','legacy'));
  p = find(ismember(t(1,:),'Prob>F','legacy'));
  for ii=2:size(terms,1)+1
    if t{ii,p}<opt.Alpha
      fprintf('''%s'' significant, F = %1.2f, p = %0.4f\n',...
        t{ii,1},t{ii,F},t{ii,p})
    else
      fprintf('''%s'' not significant, F = %1.2f, p = %0.4f\n',...
        t{ii,1},t{ii,F},t{ii,p})
    end
  end
end

function d = orthogonal_design(nRows,nLevels)
% ORTHOGONAL_DESIGN - help function to create matrices with factor values
% assuming an orthogonal design (ie, there is data for each possible
% pairing of subconditions).
% Example: If you have the factors target/nontarget, and electrode
% (Fz, Cz, Pz), and you measure the effect of these variables on 12
% subjects, you would get a 12 x 6 (=2*3) matrix wherein the rows represent
% subjects, and the columns are:
% (1)target-Fz (2)nontarget-Fz (3)target-Cz (4)nontarget-Cz (5)target-Pz (6)nontarget-Pz
% The respective function call would be orthogonal_design(12,[2 3])
%
% Synopsis:
%   D = ORTHOGONAL_DESIGN(NROWS,NLEVELS)
%
% Arguments:
%   NROWS  : number of measurements of each factor (eg number of subjects)
%   NLEVELS: a vector with each field specifying the number of levels of
%     each factor
%
% Returns:
%   A struct with the following fields
%   .mat: a cell array with the corresponding matrices for each variable
%   (except the first)
%   .anova: vector notation of the factor matrices which you can feed
%   directly into anovan()
nFactors = numel(nLevels);
nCols = prod(nLevels);
d = struct('mat',zeros(nRows,nCols,nFactors),'anova',[],'anova_mat',[]);

% Construct first row and then copy n times
for jj=1:nFactors
  repLev = nCols / prod(nLevels(jj:end)); % Number of repeats of a levels

  if jj<nFactors   % Number of repeats of the sequence
    repSeq = prod(nLevels(jj+1:end));
  else repSeq=1;
  end

  row = [];
  for rr=1:repSeq
    for nn=1:nLevels(jj)
      row = [row repmat(nn,[1 repLev])];
    end
  end
  d.mat(1,:,jj)=row;
  %% Extend to all rows
  d.mat(:,:,jj) = repmat(row,[nRows 1]);
end

for jj=1:nFactors
  dd = d.mat(:,:,jj);
  d.anova{jj} = dd(:);
  d.anova_mat = [d.anova_mat dd(:)];
end
end

end


function vals = polys_vals(polys,ts,tt,r)
idx = 1;
N = length(tt);
vals = zeros(1,N);
for i = 1:N
    t = tt(i);
    if t<ts(idx)
        vals(i) = 0;
    else
        while idx<length(ts) && t>ts(idx+1)+0.0001
            idx = idx+1;
        end
        vals(i) = poly_val(polys(:,idx),t,r);
    end
end
end


function matrix_exponential_test ( )

%*****************************************************************************80
%
%% MATRIX_EXPONENTIAL_TEST tests the MATRIX_EXPONENTIAL library.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    05 March 2013
%
%  Author:
%
%    John Burkardt
%
  addpath ( '../c8lib' )
  addpath ( '../r8lib' )
  addpath ( '../test_matrix_exponential' )

  timestamp ( );
  fprintf ( 1, '\n' );
  fprintf ( 1, 'MATRIX_EXPONENTIAL_TEST:\n'  );
  fprintf ( 1, '  MATLAB version\n' );
  fprintf ( 1, '  Test the MATRIX_EXPONENTIAL library.\n' );
  fprintf ( 1, '  The R8LIB library is needed.\n' );
  fprintf ( 1, '  The test needs the TEST_MATRIX_EXPONENTIAL library.\n' );

  matrix_exponential_test01 ( );
  matrix_exponential_test02 ( );
%
%  Terminate.
%
  fprintf ( 1, '\n' );
  fprintf ( 1, 'MATRIX_EXPONENTIAL_TEST:\n' );
  fprintf ( 1, '  Normal end of execution.\n' );
  fprintf ( 1, '\n' );
  timestamp ( );

  rmpath ( '../c8lib' )
  rmpath ( '../r8lib' )
  rmpath ( '../test_matrix_exponential' )

  return
end


function [x,fval,exitflag,info,Opt] = opti_fmincon(fun,x0,A,b,Aeq,beq,lb,ub,nonlcon,opts)
%OPTI_FMINCON Solve a NLP using an OPTI NLP Solver (Matlab Overload)
%
%   [x,fval,exitflag,info] = opti_fmincon(fun,x0,A,b,Aeq,beq,lb,ub,nonlcon)
%   solves the constrained nonlinear optimization min f(x) where fun is
%   the nonlinear function to be minimized [fun(x)], starting at x0. A,b
%   are linear inequality constraints, Aeq,beq are linear equality
%   constraints, lb,ub are decision variable bounds and nonlcon are the
%   nonlinear constraints in Matlab form.
%
%   [x,fval,exitflag,info] = opti_fmincon(fun,...,opts) allows the
%   user to specify optiset options. This includes specifying a solver via
%   the 'solver' field of optiset.
%
%   [x,...,info,Opt] = opti_fmincon(fun,...) returns the internally
%   built OPTI object.

%   Copyright (C) 2011 Jonathan Currie (IPL)

%Handle missing arguments
if nargin < 10, opts = optiset; end
if nargin < 9, nonlcon = []; end
if nargin < 8, ub = []; end
if nargin < 7, lb = []; end
if nargin < 6, beq = []; end
if nargin < 5, Aeq = []; end
if nargin < 4, b = []; end
if nargin < 3, A = []; end
if nargin < 2, error('You must supply at least 2 arguments to opti_fmincon'); end

%Opti enforces a column for x0, so make sure it is here too
if(size(x0,2) > 1), x0 = x0(:); end

%Sort out Fun + Grad
[f,g] = detGrad(fun,x0);

%Sort out NLCON
[nlcon,nlrhs,nle] = detNlcon(nonlcon,x0);

%Build OPTI Object
Opt = opti('fun',f,'grad',g,'nlmix',nlcon,nlrhs,nle,'ineq',A,b,'eq',Aeq,beq,'bounds',lb,ub,'x0',x0,'options',opts);

%Solve
[x,fval,exitflag,info] = solve(Opt);


%compute dtailinflang
function [data,units]=compute_dtailinflang(trx,n)

larvae=trx.exp2flies{n};
numlarvae=numel(larvae);
%absdtailheadang=cell(1,numlarvae);
dtailinflang=cell(1,numlarvae);
for i=1:numlarvae
    larva=larvae(i);
    dtailinflang{1,i}=modrange(diff(trx(larva).tailinflang),-pi,pi)./trx(larva).dt;
    % KB: this angle distance didn't make sense to me, changed to one that
    % made sense to me
    % dtailinflang{1,i}=(mod1(trx(larva).tailinflang(2:end)-trx(larva).tailinflang(1:end-1),pi)-pi)./trx(larva).dt;
end

units=parseunits('rad/s');
data=dtailinflang;


function  [mori,hkl,omega,sigma] = CSL(sigma,CS,varargin)
% coincidence site lattice misorientations for cubic symmetry
%
% Syntax
%   q = CSL(sigma,CS)
%
% Input
%  sigma - order of coincidence site lattice misorientation
%  CS - @crystalSymmetry
%
% Options
%  delta    - search radius around angle or axis
%  maxsigma -
%
% Output
%  o - @orientation
%

if nargin < 2 || ~isa(CS,'crystalSymmetry')
  error('Starting with MTEX 4.2 the second argument to CSL should be crystal symmetry.')
end

% we generate first all CSL misorientations up to sigma = 60
csl = generateCubicCSL(varargin{:});
% and then select those we are interested in
ndx = find( ismember([csl.sigma],sigma));

mori = orientation(CS,CS);
hkl = zeros(0,3);
omega = []; sigma = [];

for k = ndx
  hkl(end+1,:) = csl(k).axis;
  omega(end+1) = csl(k).angle;
  sigma(end+1) = csl(k).sigma;
  mori(end+1) = orientation.byAxisAngle(vector3d(hkl(end,:)),omega(end),CS,CS);
end

[mori,id] = unique(mori);
sigma = sigma(id);

end

function csl = generateCubicCSL(varargin)  % only cubic

csl = struct('sigma',{},'axis',{},'angle',{});

maxsigma = get_option(varargin,'maxsigma',60);

% heuristic
for u=0:5
  for v=0:5
    for w=0:5
      maxis = [u v w];
      %if all(maxis==0), continue; end

      % make sure u,v,w have no common divisor
      if gcd(u,gcd(v,w))~=1, continue; end

      for m=1:10

        N = sum(mod([maxis m],2));

        if mod(N,2)
          alpha = 1;
        else
          alpha = N;
        end

        % compute the energie
        sigma = sum([maxis m].^2)/alpha;

        % compute rotational angle
        omega =  2*atan( sqrt( sum(maxis.^2) )/m );

        % store values
        if omega < 63*degree && sigma < maxsigma
          csl(end+1).angle = omega;
          csl(end).axis = maxis;
          csl(end).sigma = sigma;
        end
      end
    end
  end
end

end


function AutoClusterTest(hObject,~)
%hfig = getParentFigure(hObject);
hfig = hObject;% pass it hFig for now

cIX = getappdata(hfig,'cIX');
gIX = getappdata(hfig,'gIX');
M = getappdata(hfig,'M');
M_0 = getappdata(hfig,'M_0');
thres_size = 10;
thres_split = getappdata(hfig,'thres_split');
thres_stimlock = 1.0;
thres_merge = getappdata(hfig,'thres_merge');
thres_silh = 0.4;

isWkmeans = getappdata(hfig,'isWkmeans');

%% kmeans
if isWkmeans,
    numK = 20;
    disp(['kmeans k = ' num2str(numK)]);
    tic
    rng('default');% default = 0, but can try different seeds if doesn't converge
    if numel(M)*numK < 10^7 && numK~=1,
        disp('Replicates = 5');
        gIX = kmeans(M,numK,'distance','correlation','Replicates',5);
    elseif numel(M)*numK < 10^8 && numK~=1,
        disp('Replicates = 3');
        gIX = kmeans(M,numK,'distance','correlation','Replicates',3);
    else
        gIX = kmeans(M,numK,'distance','correlation');
    end
    toc
    SaveCluster_Direct(hfig,cIX,gIX,'k=20');
end
[gIX, numU] = SqueezeGroupIX(gIX);

%% pushbutton_iter_split(hObject,~);
disp('iter. split all...');
I_rest = [];
iter = 1;
gIX_last = gIX;
I_clean_last = cIX;
cIX = [];
gIX = [];
for i = 1:numU,
    disp(['i = ' num2str(i)]);
    ix = gIX_last == i;
    IX = I_clean_last(ix);
    M_s = M_0(IX,:);
    [I_rest,cIX,gIX,numU] = CleanClus(M_s,IX,I_rest,cIX,gIX,numU,1-thres_split,thres_size);
end
[gIX, ~] = SqueezeGroupIX(gIX);
if isempty(gIX),
    errordlg('nothing to display!');
    return;
end
SaveCluster_Direct(hfig,cIX,gIX,['clean_round' num2str(iter)]);
SaveCluster_Direct(hfig,I_rest,ones(length(I_rest),1),['rest_round' num2str(iter)]);

[gIX, numU] = Merge_direct(thres_merge,M_0,cIX,gIX);

%% rank by stim-lock
disp('stim-lock');
UpdateIndices(hfig,cIX,gIX,numU);
[gIX,rankscore] = RankByStimLock_Direct(hfig,gIX,numU);
disp('ranking complete');
% and threshold
IX = find(rankscore<thres_stimlock);
ix = ismember(gIX,IX);
gIX = gIX(ix);
cIX = cIX(ix);
UpdateIndices(hfig,cIX,gIX);

%% Regression with the centroid of each cluster
[cIX,gIX,~] = AllCentroidRegression_direct(hfig);
disp('auto-reg-clus complete');

[gIX, numU] = Merge_direct(thres_merge,M_0,cIX,gIX);
SaveCluster_Direct(hfig,cIX,gIX,'clean_round2');

%% Silhouette
disp('silhouette analysis');
gIX_last = gIX;
for i = 1:numU,
    disp(['i = ' num2str(i)]);
    IX = find(gIX_last == i);
    cIX_2 = cIX(IX);
    M_s = M_0(cIX_2,:);
    % try k-means with k=2, see whether to keep
    gIX_ = kmeans(M_s,2,'distance','correlation');
    silh = silhouette(M_s,gIX_,'correlation');
    if mean(silh)>thres_silh,
        % keep the k-means k=2 subsplit
        disp('split');
        gIX(IX) = gIX_ + numU; % reassign (much larger) gIX
    end
end
[gIX, ~] = SqueezeGroupIX(gIX);

%% rank by stim-lock ?? bug?
% disp('stim-lock');
% M = M_0(cIX,:);
% [gIX,rankscore] = RankByStimLock_Direct(hfig,cIX,gIX,M,numU);
% disp('ranking complete');
% % and threshold
% IX = find(rankscore<thres_stimlock);
% ix = ismember(gIX,IX);
% gIX = gIX(ix);
% cIX = cIX(ix);
%
% [gIX, ~] = Merge_direct(thres_merge,M_0,cIX,gIX);

% size threshold
thres_size = getappdata(hfig,'thres_size');
[cIX, gIX, numU] = ThresSize(cIX,gIX,thres_size);

%% update GUI
if isempty(gIX),
    errordlg('nothing to display!');
    return;
end
UpdateIndices(hfig,cIX,gIX,numU);
RefreshFigure(hfig);

SaveCluster_Direct(hfig,cIX,gIX,'clean_round3');
beep;

end

%% Functions
function pushbutton_thressize_Callback(hObject,~)
hfig = getParentFigure(hObject);
cIX = getappdata(hfig,'cIX');
gIX = getappdata(hfig,'gIX');
thres_size = getappdata(hfig,'thres_size');
[cIX, gIX, numU] = ThresSize(cIX,gIX,thres_size);
UpdateIndices(hfig,cIX,gIX,numU);
RefreshFigure(hfig);
end

function edit_sizethres_Callback(hObject,~)
str = get(hObject,'String');
if ~isempty(str),
    temp = textscan(str,'%f');
    thres_size = temp{:};
end
hfig = getParentFigure(hObject);
setappdata(hfig,'thres_size',thres_size);
end

function [cIX, gIX, numU] = ThresSize(cIX,gIX,thres_size)
U = unique(gIX);
numU = length(U);
for i=1:numU,
    if length(find(gIX==U(i)))<thres_size,
        cIX(gIX==U(i)) = [];
        gIX(gIX==U(i)) = [];
    end
end
[gIX, numU] = SqueezeGroupIX(gIX);
end

function [gIX, numU] = Merge_direct(thres_merge,M_0,cIX,gIX)
M = M_0(cIX,:);
[gIX, numU] = HierClus(M,gIX);
U = unique(gIX);
M = M_0(cIX,:);
[C,D] = FindCentroid_Direct(gIX,M);
i = 1;
while i<numU,
    c = corr(C(i,:)',C(i+1,:)');
    if c > thres_merge,
        IX = find(gIX == U(i+1));
        gIX(IX)=U(i); %#ok<*FNDSB>
        U = unique(gIX);
        numU = length(U);

        IX = find(gIX == U(i));
        M_s = M(IX,:);
        [~,C1,~,D1] = kmeans(M_s,1,'distance','correlation');
        C(i,:) = C1;
        D(i) = mean(D1);
        C(i+1,:) = [];
        D(i+1) = [];
    else
        i = i+1;
    end
end
[gIX, numU] = HierClus(M,gIX);
disp('merging complete');
end

function checkbox_wkmeans_Callback(hObject,~)
hfig = getParentFigure(hObject);
setappdata(hfig,'isWkmeans',get(hObject,'Value'));
end
function pushbutton_merge_Callback(hObject,~)
% disp('merging...');
hfig = getParentFigure(hObject);
cIX = getappdata(hfig,'cIX');
gIX = getappdata(hfig,'gIX');
M = getappdata(hfig,'M');
U = unique(gIX);
numU = length(U);
[C,D] = FindCentroid(hfig);

thres_merge = getappdata(hfig,'thres_merge');

i = 1;
while i<numU,
    c = corr(C(i,:)',C(i+1,:)');
    if c > thres_merge,
        IX = find(gIX == U(i+1));
        gIX(IX)=U(i);
        U = unique(gIX);
        numU = length(U);

        IX = find(gIX == U(i));
        M_s = M(IX,:);
        [~,C1,~,D1] = kmeans(M_s,1,'distance','correlation');
        C(i,:) = C1;
        D(i) = mean(D1);
        C(i+1,:) = [];
        D(i+1) = [];
    else
        i = i+1;
    end
end

if numU>1,
    [gIX, numU] = HierClus(M,gIX);
end

UpdateIndices(hfig,cIX,gIX,numU);
RefreshFigure(hfig);
disp('merging complete');
end

function edit_mergethres_Callback(hObject,~)
str = get(hObject,'String');
temp = textscan(str,'%f',1);
hfig = getParentFigure(hObject);
setappdata(hfig,'thres_merge',temp{:});
end

function pushbutton_iter_split(hObject,~)
hfig = getParentFigure(hObject);
cIX = getappdata(hfig,'cIX');
gIX = getappdata(hfig,'gIX');
numU = getappdata(hfig,'numK');
thres_split = getappdata(hfig,'thres_split');
M_0 = getappdata(hfig,'M_0');

disp('iter. split all, beep when done...');
thres_size = 10;
thres_H = thres_split;
% thres_H = [0.2;0.15;0.1;0.05]; % could have more rounds...

% initialization
I_rest = [];
% loop
tic
for round = 1:length(thres_H),
    disp(['round ' num2str(round) ', numU = ' num2str(numU)]);
    dthres = 1-thres_H(round);
    gIX_last = gIX;
    I_clean_last = cIX;
    cIX = [];
    gIX = [];
    for i = 1:numU,
        disp(['i = ' num2str(i)]);
        ix = find(gIX_last == i);
        %         IX = ix;
        IX = I_clean_last(ix);
        M_s = M_0(IX,:);
        [I_rest,cIX,gIX,numU] = CleanClus(M_s,IX,I_rest,cIX,gIX,numU,dthres,thres_size);
        %         cIX = I_clean_last(I_clean);
    end

    [gIX, numU] = SqueezeGroupIX(gIX);
    SaveCluster_Direct(hfig,cIX,gIX,['clean_round' num2str(round)]);

    SaveCluster_Direct(hfig,I_rest,ones(length(I_rest),1),['rest_round' num2str(round)]);
end
toc
beep
end

function [I_rest,I_clean,gIX_clean,numU] = CleanClus(M_s,IX,I_rest,I_clean,gIX_clean,numU,dthres,thres_size)
I_clean_s = [];

% find numK_s for kmeans
kmax = min(round(size(M_s,1)/thres_size),30);
% try numK_s = 1
numK_s = 1;
rng('default');
[gIX_s,~,~,D] = kmeans(M_s,numK_s,'distance','correlation');
Dist = min(D,[],2);
if mean(Dist)>dthres,
    % try numK_s = kmax
    numK_s = kmax;
    rng('default');
    [gIX_s,~,~,D] = kmeans(M_s,numK_s,'distance','correlation');
    Dist = min(D,[],2);
    if mean(Dist)<dthres, % find in between value for numK_s
        numK_s = 2;
        while 1,
            % kmeans-cluster by numK_s
            rng('default');
            [gIX_s,~,~,D] = kmeans(M_s,numK_s,'distance','correlation');
            Dist = min(D,[],2);
            if mean(Dist)<dthres,
                break;
            end

            if numK_s < kmax,
                numK_s = numK_s+1;
                disp(['numK_s = ' num2str(numK_s)]);
            else % numK_s = kmax;
                break;
            end
        end
    else disp(['numK_s = ' num2str(numK_s)]);
    end
end
% have numK_s that makes mean(Dist) < thres, or numK_s = kmax

for i = 1:numK_s,
    IX_s = find(gIX_s == i);
    if length(IX_s)>thres_size, % cluster big enough to start
        dst = Dist(IX_s);
        if mean(dst) < dthres,
            I_clean_s = [I_clean_s; IX_s]; %#ok<*AGROW>
            gIX_clean = [gIX_clean; gIX_s(IX_s)+double(numU)];
        else
            ix = find(dst<dthres); % clean
            if length(ix)>=thres_size, % clean cluster still big enough
                I = IX_s(ix);
                I_clean_s = [I_clean_s; I];
                gIX_clean = [gIX_clean; gIX_s(I)+double(numU)];
            end
        end
    end
end

numU = numU + numK_s;
I_rest_s = setdiff(1:size(M_s,1),I_clean_s);
I_rest = [I_rest; IX(I_rest_s)];
I_clean = [I_clean; IX(I_clean_s)];
end

function edit_splitthres_Callback(hObject,~)
str = get(hObject,'String');
temp = textscan(str,'%f',1);
hfig = getParentFigure(hObject);
setappdata(hfig,'thres_split',temp{:});
end

%% Internal functions

function UpdateClusGroupID(hfig,clusgroupID,new_clusgroupID,norefresh) %#ok<INUSD>
% save/update old Cluster into ClusGroup before exiting,
% as Cluster is the variable handled in hfig but not saved elsewhere
ClusGroup = getappdata(hfig,'ClusGroup');
Cluster = getappdata(hfig,'Cluster');
i_fish = getappdata(hfig,'i_fish');

% update into workspace
ClusGroup{clusgroupID} = Cluster;
setappdata(hfig,'ClusGroup',ClusGroup);
global VAR;
VAR(i_fish).ClusGroup = CurrentClusGroup(hfig);

% load new 'Cluster'
Cluster = ClusGroup{new_clusgroupID};
setappdata(hfig,'Cluster',Cluster);
setappdata(hfig,'clusgroupID',new_clusgroupID);

% update GUI: hclusgroupmenu
global hclusgroupmenu hclusgroupname;
if ishandle(hclusgroupmenu),
    menu = MakeNumberedMenu(VAR(i_fish).ClusGroupName);
    set(hclusgroupmenu,'String',menu,'Value',new_clusgroupID+1);
    set(hclusgroupname,'String',VAR(i_fish).ClusGroupName(new_clusgroupID));
end

if ~exist('norefresh','var'),
    if numel(Cluster) == 0, % i.e. for newly created ClusGroup
        SaveCluster(hfig,'new');
    else % load this ClusGroup
        clusID = 1;
        UpdateClusID(hfig,clusID);
    end
end
end

function ClusGroup = CurrentClusGroup(hfig)
ClusGroup = getappdata(hfig,'ClusGroup');
Cluster = getappdata(hfig,'Cluster');
clusgroupID = getappdata(hfig,'clusgroupID');
ClusGroup{clusgroupID} = Cluster;
setappdata(hfig,'ClusGroup',ClusGroup);
end

function [Cluster,clusID] = SaveCluster(hfig,state,clusheader,name)
cIX = getappdata(hfig,'cIX');
gIX = getappdata(hfig,'gIX');
absIX = getappdata(hfig,'absIX');
Cluster = getappdata(hfig,'Cluster');

if strcmp(state,'current'),
    clusID = getappdata(hfig,'clusID');
else %if strcmp(state,'new'),
    clusID = numel(Cluster)+1;
    if ~exist('name','var'),
        name = getappdata(hfig,'newclusname');
    end
    if ~exist('clusheader','var'),
        clusheader = getappdata(hfig,'clusheader');
    end
    Cluster(clusID).name = [clusheader name];
end

cIX_abs = absIX(cIX);

Cluster(clusID).cIX_abs = cIX_abs;
Cluster(clusID).gIX = gIX;
Cluster(clusID).numel = length(cIX_abs);
U = unique(gIX);
numU = length(U);
Cluster(clusID).numK = numU;

setappdata(hfig,'Cluster',Cluster);
UpdateClusID(hfig,clusID);
disp('cluster saved');
end

function [Cluster,clusID] = SaveCluster_Direct(hfig,cIX,gIX,name) %,clusheader,name)
new_clusgroupID = 1;
clusgroupID = getappdata(hfig,'clusgroupID');
UpdateClusGroupID(hfig,clusgroupID,new_clusgroupID);

if ~exist('cIX','var'),
    cIX = getappdata(hfig,'cIX');
end
if ~exist('gIX','var'),
    gIX = getappdata(hfig,'gIX');
end

Cluster = getappdata(hfig,'Cluster');
absIX = getappdata(hfig,'absIX');
cIX_abs = absIX(cIX);

clusID = numel(Cluster)+1;
% if ~exist('name','var'),
%     name = getappdata(hfig,'newclusname');
% end
% if ~exist('clusheader','var'),
%     clusheader = getappdata(hfig,'clusheader');
% end
Cluster(clusID).name = name; %[clusheader name];
Cluster(clusID).cIX_abs = cIX_abs;
Cluster(clusID).gIX = gIX;
Cluster(clusID).numel = length(cIX);
Cluster(clusID).numK = length(unique(gIX));

setappdata(hfig,'Cluster',Cluster);
UpdateClusID(hfig,clusID);
disp('cluster saved');
end

function UpdateClusID(hfig,clusID)
Cluster = getappdata(hfig,'Cluster');

% save
setappdata(hfig,'clusID',clusID);
% update GUI
global hclusname hclusmenu;
set(hclusname,'String',Cluster(clusID).name);
menu = MakeNumberedMenu({Cluster.name});
set(hclusmenu,'String', menu,'Value',clusID+1);
numK = Cluster(clusID).numK;
gIX = Cluster(clusID).gIX;

% convert absolute index to index used for this dataset
absIX = getappdata(hfig,'absIX');
cIX_abs = Cluster(clusID).cIX_abs;
[~,cIX] = ismember(cIX_abs,absIX);

if ~isempty(find(cIX==0,1)),
    errordlg('cell index out of bound for currently loaded dataset');
    IX = cIX==0;
    cIX(IX) = [];
    gIX(IX) = [];
end

UpdateIndices(hfig,cIX,gIX,numK);
RefreshFigure(hfig);
end

function menu = MakeNumberedMenu(name) % e.g. name = {Cluster.name} (note {})
menu = [{'(choose)'},name];
for j=2:length(menu),menu(j)={[num2str(j-1) ': ' menu{j}]};end
end

function [gIX, numU] = HierClus(M,gIX,isplotfig) %#ok<INUSD>
[gIX, numU] = SqueezeGroupIX(gIX);
[C,~] = FindCentroid_Direct(gIX,M);
D = pdist(C,'correlation');
tree = linkage(C,'average','correlation');
leafOrder = optimalleaforder(tree,D);

if numU>1,
    if exist('isplotfig','var'),
        figure('Position',[100 100 600 600]);
        %             subplot(1,3,1);
        %             CORR = corr(C');
        %             CorrPlot(CORR);
        %
        %             subplot(1,3,2);
        dendrogram(tree,numU,'orientation','right','reorder',leafOrder);
        set(gca,'YDir','reverse');
        set(gca,'XTick',[]);

        %             subplot(1,3,3);
        %             C2 = C(leafOrder,:);
        %             CORR2 = corr(C2');
        %             CorrPlot(CORR2);
    end
    % sort for uniform colorscale
    temp = zeros(size(gIX));
    for i = 1:numU,
        temp(gIX==leafOrder(i)) = i; % = T(i) for clusters segmented from tree
    end
    gIX = temp;
end
end

function gIX = HierClusDirect(C,gIX,numU)
D = pdist(C,'correlation');
tree = linkage(C,'average','correlation');
leafOrder = optimalleaforder(tree,D);

% sort for uniform colorscale
temp = zeros(size(gIX));
for i = 1:numU,
    temp(gIX==leafOrder(i)) = i; % = T(i) for clusters segmented from tree
end
gIX = temp;
end

function [gIX, numK] = SqueezeGroupIX(gIX)
U = unique(gIX);
numK = length(U);
for i = 1:numK,
    old = U(i);
    gIX(gIX==old) = i;
end
end

% frequently used, updates cell-index,group-index,cluster-number. set-operations included in here.
function UpdateIndices(hfig,cIX,gIX,numK)
global hback hopID;
if ~exist('gIX','var'),
    gIX = getappdata(hfig,'gIX');
end
if ~exist('cIX','var'),
    cIX = getappdata(hfig,'cIX');
end

% update cache
bC = getappdata(hfig,'bCache');
cIX_last = getappdata(hfig,'cIX');
gIX_last = getappdata(hfig,'gIX');
if ~(isequal(cIX_last,cIX) && isequal(gIX_last,gIX)),
    bC.cIX = [cIX_last,bC.cIX];
    bC.gIX = [gIX_last,bC.gIX];
    bC.numK = [getappdata(hfig,'numK'),bC.numK];
    set(hback,'enable','on');
    if length(bC.cIX)>20,
        bC.cIX(end) = [];
        bC.gIX(end) = [];
        bC.numK(end) = [];
    end
end

% set operations, if applicable
opID = getappdata(hfig,'opID');
if opID ~= 0,
    switch opID,
        case 1,
            disp('union');
            [~,ia,ib] = union(cIX_last,cIX,'stable');
            IX = vertcat(cIX_last(ia),cIX(ib));% IX;
        case 2,
            disp('intersect');
            [IX,ia,~] = intersect(cIX_last,cIX);
            ib = [];
        case 3,
            disp('setdiff');
            [IX,ia] = setdiff(cIX_last,cIX);
            ib = [];
        case 4,
            disp('rev setdiff');
            % swap sequence, then same as opID==3
            temp = cIX;
            cIX = cIX_last;
            cIX_last = temp;
            temp = gIX;
            gIX = gIX_last;
            gIX_last = temp;
            [IX,ia] = setdiff(cIX_last,cIX);
            ib = [];
        case 5,
            disp('setxor');
            [IX,ia,ib] = setxor(cIX_last,cIX);
        case 6,
            disp('smartUnion');
            CIX = vertcat(cIX_last,cIX);
            GIX = [gIX_last;gIX+max(gIX_last)]; % gIX to match
            M_0 = getappdata(hfig,'M_0');
            [cIX,gIX,numK] = SmartUnique(CIX,GIX,M_0(CIX,:));
    end
    if opID<6,
        if ~isempty(IX),
            cIX = IX;
            gIX = vertcat(gIX_last(ia),gIX(ib)+max(gIX_last(ia)));
            numK = length(unique(gIX));
            %         [gIX, numK] = SqueezeGroupIX(gIX);
        else
            errordlg('operation result is empty set!')
            waitforbuttonpress;
        end
    end
    set(hopID,'Value',1,'BackgroundColor',[1,1,1]); % reset
    setappdata(hfig,'opID',0);
end

setappdata(hfig,'bCache',bC);
setappdata(hfig,'cIX',cIX);
setappdata(hfig,'gIX',gIX);

M = GetTimeIndexedData(hfig);
setappdata(hfig,'M',M);

if exist('numK','var'),
    setappdata(hfig,'numK',double(numK));
end

%% Resets: reset flags the NEXT time this function is called (so they only apply to this particular plot)
% handle rankID: >=2 means write numbers as text next to colorbar
% first UpdateIndices sets rankID to 100, second sets back to 0
rankID = getappdata(hfig,'rankID');
if rankID>=2,
    if rankID==100,
        setappdata(hfig,'rankID',0);
    else
        setappdata(hfig,'rankID',100);
    end
end

% toggle 'isWeighAlpha'
isWeighAlpha = getappdata(hfig,'isWeighAlpha');
if isWeighAlpha == 1,
    setappdata(hfig,'isWeighAlpha',100);
elseif isWeighAlpha == 100,
    setappdata(hfig,'isWeighAlpha',0);
end

% FindCentroid reset:
setappdata(hfig,'Centroids',[]);
setappdata(hfig,'D_ctrd',[]);
end

% frequently used, 2 plotting functions are outside ('DrawTimeSeries.m' and 'DrawCellsOnAnatProj.m')
function RefreshFigure(hfig)
watchon; drawnow;
isPopout = 0; % with down-sampling in plots

% clean-up canvas
allAxesInFigure = findall(hfig,'type','axes');
if ~isempty(allAxesInFigure)
    delete(allAxesInFigure);
end

figure(hfig);
h1 = axes('Position',[0.05, 0.04, 0.55, 0.83]);
h2 = axes('Position',[0.63, 0.04, 0.35, 0.83]);

isCentroid = getappdata(hfig,'isCentroid');
isRefAnat = getappdata(hfig,'isRefAnat');

isPlotLines = 0; %getappdata(hfig,'isPlotLines');
isPlotBehavior = 1; %getappdata(hfig,'isPlotBehavior');

% double-check if cIX is valid
cIX = getappdata(hfig,'cIX');
if isempty(cIX),
    errordlg('empty set!');
    % GO BACK to the last step (presumably not empty)
    pushbutton_back_Callback(h1); % using h1 instaed of the usual 'hObject'
    return;
end

% left subplot
axes(h1);
DrawTimeSeries(hfig,h1,isPopout,isCentroid,isPlotLines,isPlotBehavior);

% right subplot
axes(h2);
DrawCellsOnAnatProj(hfig,isRefAnat,isPopout);
watchoff;
end

function [C,D] = FindCentroid_Direct(gIX,M)
U = unique(gIX);
numU = length(U);
C = zeros(numU,size(M,2));
D = zeros(numU,1);
for i=1:numU,
    IX = find(gIX == U(i));
    if length(IX)==1,
        C(i,:) = M(IX,:);
        D(i) = 1;
    else
        M_s = M(IX,:);
        [~,C1,~,D1] = kmeans(M_s,1,'distance','correlation');
        C(i,:) = C1;
        D(i) = mean(D1);
    end
end
end

function UpdateTimeIndex(hfig,isSkipcIX) %#ok<INUSD>
% input params
isAvr = getappdata(hfig,'isAvr');
isRawtime = getappdata(hfig,'isRawtime');
stimrange = getappdata(hfig,'stimrange');
% load
timelists = getappdata(hfig,'timelists');
periods = getappdata(hfig,'periods');
fishset = getappdata(hfig,'fishset');

if fishset == 1,
    if isAvr,
        tIX = 1:periods;
    else
        tIX = timelists{1};
    end

else % fishset>1,
    if isAvr,
        tIX = [];
        for i = 1:length(stimrange),
            ix = stimrange(i);
            i_start = sum(periods(1:ix-1)); % if ix-1<1, sum = 0
            tIX = horzcat(tIX,(i_start+1:i_start+periods(ix)));
            %             tIX = vertcat(tIX,(i_start+1:i_start+periods(ix))');
        end
    else % full range
        if ~isRawtime,
            tIX = cat(2, timelists{stimrange});
        else
            tIX = sort(cat(2, timelists{stimrange}));
        end
    end
end

setappdata(hfig,'tIX',tIX);

% set Matrices to hold time-series
M_0 = GetTimeIndexedData(hfig,'isAllCells');
setappdata(hfig,'M_0',M_0);
if ~exist('isSkipcIX','var'),
    cIX = getappdata(hfig,'cIX');
    setappdata(hfig,'M',M_0(cIX,:));
end
end

function [M,behavior,stim] = GetTimeIndexedData(hfig,isAllCells) %#ok<INUSD>
%{
% naming convention used:
M = GetTimeIndexedData(hfig);
M_0 = GetTimeIndexedData(hfig,'isAllCells');
%}

isZscore = getappdata(hfig,'isZscore');
% main data input
if ~isZscore,
    cellResp = getappdata(hfig,'CellResp');
    cellRespAvr = getappdata(hfig,'CellRespAvr');
else
    cellResp = getappdata(hfig,'CellRespZ');
    cellRespAvr = getappdata(hfig,'CellRespAvrZ');
end
Behavior_full = getappdata(hfig,'Behavior_full');
BehaviorAvr = getappdata(hfig,'BehaviorAvr');
stim_full = getappdata(hfig,'stim_full');
stimAvr = getappdata(hfig,'stimAvr');
% other params
isAvr = getappdata(hfig,'isAvr');
cIX = getappdata(hfig,'cIX');
tIX = getappdata(hfig,'tIX');

%% set data
if isAvr,
    if exist('isAllCells','var'),
        M = cellRespAvr(:,tIX);
    else
        M = cellRespAvr(cIX,tIX);
    end
    behavior = BehaviorAvr(:,tIX);
    stim = stimAvr(:,tIX);
else
    if exist('isAllCells','var'),
        M = cellResp(:,tIX);
    else
        M = cellResp(cIX,tIX);
    end
    behavior = Behavior_full(:,tIX);
    stim = stim_full(:,tIX);
end

setappdata(hfig,'behavior',behavior);
setappdata(hfig,'stim',stim);
end

function closefigure_Callback(hfig,~)
global EXPORT_autorecover;
EXPORT_autorecover = getappdata(hfig);
end

function fig = getParentFigure(fig)
% if the object is a figure or figure descendent, return the figure. Otherwise return [].
while ~isempty(fig) && ~strcmp('figure', get(fig,'type'))
    fig = get(fig,'parent');
end
end

function runscript(flag_script,var_script)
switch flag_script
    case 'push_cIX_gIX'
        UpdateIndices(var_script{:});
        RefreshFigure(var_script{1});
end
end


clear all; close all; clc;

M = fixedTTrankfactory([5 5 5], [1 5 5 1]);

checkmanifold(M);


function [mLMagsig, mHMagsig, fLZmax, fLZmean, fLZmin, fHZmax, fHZmean, fHZmin] =...
    plot_Magsig(mCat1, mCat2 , fPeriod1, fPeriod2, fBinning);
% function [mLMagsig, mHMagsig, fLZmax, fLZmean, fLZmin, fHZmax, fHZmean, fHZmin] =
%           plot_Magsig(mCat1, mCat2 , fPeriod1, fPeriod2, fBinning);
%-----------------------------------------------------------------------------------
% Calculate and plot magnitude signature
%
% Incoming variables:
% mCat1: EQ catalog period 1 (background)
% mCat2: EQ catalog period 2 (foreground)
% fPeriod1 : Length of time period 1 in dec. days
% fPeriod2 : Length of time period 2 in dec. days
% fBinning : Time length of bins in dec. years
%
% Outgoing variables:
% mLMagsig : Matrix of magnitude signature "and below"
% mHMagsig : Matrix of magnitude signature "and above"
% fLZmax   : maximum z-value of mLMagsig
% fHZmax   : maximum z-value of mHMagsig
% fLZmean  : mean z-value of mLMagsig
% fHZmean  : mean z-value of mHMagsig
% fLZmin   : minimum z-value of mLMagsig
% fHZmin   : minimum z-value of mHMagsig
%
% Author: J. Woessner, woessner@seismo.ifg.ethz.ch
% last update: 30.09.02

report_this_filefun(mfilename('fullpath'));

%% Intialize
%% Binnings of time periods
vBinPer1 = min(mCat1(:,3)):fBinning:max(mCat1(:,3));
vBinPer2 = min(mCat2(:,3)):fBinning:max(mCat2(:,3));

fMinMag = floor(min([min(mCat1(:,6)) min(mCat2(:,6))]));
fMaxMag = ceil(max([max(mCat1(:,6)) max(mCat2(:,6))]));
vLMagsig = zeros(size(fMinMag:0.1:fMaxMag));
vHMagsig = zeros(size(fMinMag:0.1:fMaxMag));
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                     Loop over all magnitude bands
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
wai = waitbar(0,'Please wait...');
set(wai,'Color',[0.8 0.8 0.8],'NumberTitle','off','Name','Percent completed');
% nmag = length(mmin:0.1:mmax);
nStep = 0;

for i = fMinMag:0.1:fMaxMag
    waitbar(nStep/length(vLMagsig));
    nStep = nStep+1;
    % disp(i)
    %%%%% START computation Magnitude signature for "magnitude and below"
    %% Datata selection
    vSel1 = mCat1(:,6) <= i;
    mTmpCat1 = mCat1(vSel1,:);
    vSel2 = mCat2(:,6) <= i;
    mTmpCat2 = mCat2(vSel2,:);
    if (isempty(mTmpCat1) | isempty(mTmpCat2))
        vLMagsig(nStep) = NaN;
        disp('Not enough data');
    else
        [vCum_TPer1, vBinPer1] = hist(mTmpCat1(:,3),vBinPer1);     %    background
        [vCum_TPer2, vBinPer2] = hist(mTmpCat2(:,3),vBinPer2);     %    foreground
        fMean1 = mean(vCum_TPer1(1:length(vBinPer1)));
        fMean2 = mean(vCum_TPer2(1:length(vBinPer2)));
        fVar1 = cov(vCum_TPer1(1:length(vBinPer1)));
        fVar2 = cov(vCum_TPer2(1:length(vBinPer2)));
        if sqrt(fVar1/length(vBinPer1)+fVar2/length(vBinPer2)) > 0
            vLMagsig(nStep) = (fMean1 - fMean2)/(sqrt(fVar1/length(vBinPer1)+fVar2/length(vBinPer2)));
        end
    end % Check on emptiness
    %%%%% END computation Magnitude signature "magnitudes and below"
    %%%%% START  computation Magnitude signature "magnitudes and above"
    %% Datata selection
    vSel1 = mCat1(:,6) >= i;
    mTmpCat1 = mCat1(vSel1,:);
    vSel2 = mCat2(:,6) >= i;
    mTmpCat2 = mCat2(vSel2,:);
    if (isempty(mTmpCat1) | isempty(mTmpCat2))
        vHMagsig(nStep) = NaN;
        disp('Not enough data');
    else
        [vCum_TPer1, vBinPer1] = hist(mTmpCat1(:,3),vBinPer1);     %    background
        [vCum_TPer2, vBinPer2] = hist(mTmpCat2(:,3),vBinPer2);     %    foreground
        fMean1 = mean(vCum_TPer1(1:length(vBinPer1)));
        fMean2 = mean(vCum_TPer2(1:length(vBinPer2)));
        fVar1 = cov(vCum_TPer1(1:length(vBinPer1)));
        fVar2 = cov(vCum_TPer2(1:length(vBinPer2)));
        if sqrt(fVar1/length(vBinPer1)+fVar2/length(vBinPer2)) > 0
            vHMagsig(nStep) = (fMean1 - fMean2)/(sqrt(fVar1/length(vBinPer1)+fVar2/length(vBinPer2)));
        end
    end % Check on emptiness
end % End for i
close(wai); % Close waitbar

%% Max, mean, min values for "and below"
vMagnitudes = fMinMag:0.1:fMaxMag;
mLMagsig = [vLMagsig' vMagnitudes'];
[fLZmax, nIndMax] = max(mLMagsig(:,1))
[fLZmin, nIndMin] = min(mLMagsig(:,1))
vLZmax = mLMagsig(nIndMax,:);
vLZmin = mLMagsig(nIndMin,:);
vSel = ~isnan(mLMagsig(:,1));
mLMagsigTmp = mLMagsig(vSel,:);
fLZmean = mean(mLMagsigTmp(:,1))

%% Max, mean, min values for "and above"
mHMagsig = [vHMagsig' vMagnitudes'];
[fHZmax, nIndMax] = max(mHMagsig(:,1))
[fHZmin, nIndMin] = min(mHMagsig(:,1))
vHZmax = mHMagsig(nIndMax,:);
vHZmin = mHMagsig(nIndMin,:);
vSel= ~isnan(mHMagsig(:,1));
mHMagsigTmp = mHMagsig(vSel,:);
fHZmean = mean(mHMagsigTmp(:,1))

% Plot
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Start First Plot %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
if exist('magsig_fig','var') &  ishandle(magsig_fig)
    set(0,'Currentfigure',magsig_fig);
    disp('Figure exists');
else
    cum_mag_fig=figure_w_normalized_uicontrolunits('tag','magsig','Name','Magnitude signature','Units','normalized','Nextplot','add','Numbertitle','off');
    cum_mag_axs=axes('tag','ax_magsig','Nextplot','add','box','off');
end
set(gca,'tag','ax_magsig','Nextplot','replace','box','off','visible','off');
%axs5=findobj('tag','ax_magsig');
%axes(axs5(1));

vZeroLine = zeros(length(vMagnitudes));
vSig99 = zeros(length(vMagnitudes),2);
vSig99(:,1) = 2.57;
vSig99(:,2) = -2.57;

%% Magnitude signature "And below"
rect = [0.15, 0.15, 0.35, 0.7];
axes('position',rect)
plot(mLMagsig(:,2), mLMagsig(:,1),'r-d');
hold on;
plot(vLZmax(1,2), vLZmax(1,1),'b*');
plot(vLZmin(1,2), vLZmin(1,1),'b*');
plot(vMagnitudes, vSig99(:,1),'g--',vMagnitudes, vSig99(:,2),'g--',vMagnitudes, vZeroLine,'b--');
%% Y-Scale for entire plot
fYmin = floor(min([vLZmin(1,1) vHZmin(1,1) -3]));
fYmax = ceil(max([vLZmax(1,1) vHZmax(1,1) 3]));
axis([fMinMag fMaxMag fYmin fYmax]);
ylabel('z-value');
xlabel('Mag. and below');
%% Magnitude signature "And above"
rect = [0.15+0.35 0.15 0.35 0.7];
axes('position',rect)
plot(mHMagsig(:,2), mHMagsig(:,1),'r-d');
hold on;
plot(vHZmax(1,2), vHZmax(1,1),'b*');
plot(vHZmin(1,2), vHZmin(1,1),'b*');
plot(vMagnitudes, vSig99(:,1),'g--',vMagnitudes, vSig99(:,2),'g--',vMagnitudes, vZeroLine,'b--');
axis([fMinMag fMaxMag fYmin fYmax]);
xlabel('Mag. and above');
set(gca,'YTick',[]);


classdef ANSGAIII < ALGORITHM
% <multi/many> <real/integer/label/binary/permutation> <constrained/none>
% Adaptive NSGA-III

%------------------------------- Reference --------------------------------
% H. Jain and K. Deb, An evolutionary many-objective optimization algorithm
% using reference-point based non-dominated sorting approach, part II:
% Handling constraints and extending to an adaptive approach, IEEE
% Transactions on Evolutionary Computation, 2014, 18(4): 602-622.
%------------------------------- Copyright --------------------------------
% Copyright (c) 2023 BIMK Group. You are free to use the PlatEMO for
% research purposes. All publications which use this platform or any code
% in the platform should acknowledge the use of "PlatEMO" and reference "Ye
% Tian, Ran Cheng, Xingyi Zhang, and Yaochu Jin, PlatEMO: A MATLAB platform
% for evolutionary multi-objective optimization [educational forum], IEEE
% Computational Intelligence Magazine, 2017, 12(4): 73-87".
%--------------------------------------------------------------------------

    methods
        function main(Algorithm,Problem)
            %% Generate the reference points and random population
            % All the reference points
            [Z,Problem.N] = UniformPoint(Problem.N,Problem.M);
            Z = sortrows(Z);
            % Distance between two consecutive reference points for the adaption
            interval = Z(1,end) - Z(2,end);
            % Initial population
            Population = Problem.Initialization();
            % Ideal point
            Zmin = min(Population(all(Population.cons<=0,2)).objs,[],1);

            %% Optimization
            while Algorithm.NotTerminated(Population)
                MatingPool = TournamentSelection(2,Problem.N,sum(max(0,Population.cons),2));
                Offspring  = OperatorGA(Problem,Population(MatingPool));
                Zmin       = min([Zmin;Offspring(all(Offspring.cons<=0,2)).objs],[],1);
                Population = EnvironmentalSelection([Population,Offspring],Problem.N,Z,Zmin);
                Z          = Adaptive(Population.objs,Z,Problem.N,interval);
            end
        end
    end
end