Home > src > softDistanceConstraint.m

# softDistanceConstraint

## PURPOSE

- parent class for soft distance constraints

## SYNOPSIS

This is a script file.

## DESCRIPTION

     - parent class for soft distance constraints

     copyright 2009-2012 Blair Armstrong, Christine Watson, David Plaut

        This file is part of SOS

        SOS is free software: you can redistribute it and/or modify
        it for academic and non-commercial purposes
        under the terms of the GNU General Public License as published by
        the Free Software Foundation, either version 3 of the License, or
        (at your option) any later version.  For commercial or for-profit
        uses, please contact the authors (sos@cnbc.cmu.edu).

        SOS 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.

## CROSS-REFERENCE INFORMATION

This function calls:

- genericConstraint - parent class for constraints
- softConstraint - parent class for soft constraints
- softDistanceConstraint - parent class for soft distance constraints
- verbosePrint - master print function that regulates cmd line output based on verbosity flags

This function is called by:

- genericConstraint - parent class for constraints
- softDistanceConstraint - parent class for soft distance constraints

## SUBFUNCTIONS

- function obj = softDistanceConstraint(varargin)
- function obj = construct2SampleSoftDistanceConstraint(obj,varargin)
- function obj = construct1SampleSoftDistanceConstraint(obj,varargin)
- function cost = initCost(obj)
- function swCost = swapCost(obj,targSample,targSampleIndex, feederdf,feederdfIndex)
- function obj = initGroupMeans(obj)
- function obj = initPairedMeans(obj)
- function obj = initGroupStdev(obj)
- function obj = initPairedStdev(obj)
- function obj = initSingleMeanandTarg(obj)
- function obj = initSingleStdevandTarg(obj)
- function swGroupMeans(obj,targSample,targSampleIndex, feederdf,feederdfIndex)
- function swPairedMeans(obj,targSample,targSampleIndex, feederdf,feederdfIndex)
- function swGroupStdev(obj,targSample,targSampleIndex, feederdf,feederdfIndex)
- function swPairedStdev(obj,targSample,targSampleIndex, feederdf,feederdfIndex)
- function swSingleMean(obj,targSample,targSampleIndex, feederdf,feederdfIndex)
- function swSingleStdev(obj,targSample,targSampleIndex, feederdf,feederdfIndex)
- function cost = minDiff(obj,x1,x2)
- function cost = maxDiff(obj,x1,x2)
- function cost = orderedMaxDiff(obj,x1,x2)
- function cost = acceptSwap(obj)
- function cost = rejectSwap(obj)
- function acceptSwapMeans(obj)
- function rejectSwapMeans(obj)
- function acceptSwapStdev(obj)
- function rejectSwapStdev(obj)
- function p = softDistanceConstraintInputParser2Sample()
- function p = softDistanceConstraintInputParser1Sample()
- function p = parse2SampleConstructorArgs(varargin)
- function p = parse1SampleConstructorArgs(varargin)

## SOURCE CODE

    0001 % - parent class for soft distance constraints
    0002 %
    0003 % copyright 2009-2012 Blair Armstrong, Christine Watson, David Plaut
    0004 %
    0005 %    This file is part of SOS
    0006 %
    0007 %    SOS is free software: you can redistribute it and/or modify
    0008 %    it for academic and non-commercial purposes
    0009 %    under the terms of the GNU General Public License as published by
    0010 %    the Free Software Foundation, either version 3 of the License, or
    0011 %    (at your option) any later version.  For commercial or for-profit
    0012 %    uses, please contact the authors (sos@cnbc.cmu.edu).
    0013 %
    0014 %    SOS is distributed in the hope that it will be useful,
    0015 %    but WITHOUT ANY WARRANTY; without even the implied warranty of
    0016 %    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    0017 %    GNU General Public License for more details.
    0018
    0019 %    You should have received a copy of the GNU General Public License
    0020 %    along with SOS (see COPYING.txt).
    0021 %    If not, see <http://www.gnu.org/licenses/>.
    0022
    0023
    0024 classdef softDistanceConstraint < softConstraint
    0025     %% creates and supports soft distance constraints
    0026     %
    0027     % This class creates softDistanceConstraint objects that measure the
    0028     % cost (in terms of minimizing or maximizing a distance of a particular stat)
    0029     % associated with the current items in a set, and subsequent to
    0030     % swapping a particular item with another items.
    0031     %
    0032     % Additional functionality and interface requirements are inherited
    0033     % from softConstraint.
    0034     %
    0035     % Currently allows for the following:
    0036     %
    0037     %   min/max/OrderedMax of Group Means, Paired means, Group Stdev,
    0038     %   Paired Stdev, Matching Mean to target, Matching stdev to target
    0039     %
    0040     %
    0041     %PROPERTIES
    0042     %     stat % the statistic to calculate
    0043     %     paired - logical indicating if paired differences or group differences are bieng used
    0044     %     comparison - handle to specific comparison calculator
    0045     %     initStats % handle to global stat initialization method
    0046     %     swStats % handle to local stat update method
    0047     %     acceptsw % handle to local accept swap method
    0048     %     rejectsw % handle to local reject swap method
    0049     %     s1 - sample1
    0050     %     s2 - sample2
    0051     %     s1Col - s1 column index
    0052     %     s2Col - s2 column index
    0053     %     s1ColName - name of column in s1
    0054     %     s2ColName - name of column in s2
    0055     %     stat1 - current stat of sample 1
    0056     %     stat2 - current stat of sample2
    0057     %     swstat1 - stat of sample 1 if swap occurs
    0058     %     swstat2 - stat of sample2 if swap occurs
    0059     %     sumx1sq % sum of the squared values, x, in an array.  Used in stdev calculation
    0060     %     meanx1 % square of the mean value of x in an array.  Used in stdev calculation
    0061     %     swpsumx1sq % swap sum of the squared values, x, in an array.  Used in stdev calculation
    0062     %     swpmeanx1 % swap square of the mean value of x in an array.  Used in stdev calculation
    0063     %     sumx2sq % sum of the squared values, x, in an array.  Used in stdev calculation
    0064     %     meanx2 % square of the mean value of x in an array.  Used in stdev calculation
    0065     %     swpsumx2sq % swap sum of the squared values, x, in an array.  Used in stdev calculation
    0066     %     swpmeanx2 % swap square of the mean value of x in an array.  Used in stdev calculation
    0067     %     targVal % target value to match for 1 sample-to-value matching
    0068     %     zTargVal % normalized target value
    0069     %
    0070     %METHODS
    0071     %   obj =softDistanceConstraint(varargin) - constructor
    0072     %   obj = construct2SampleSoftDistanceConstraint(varargin) % creates a constraint object for cases wehre 2 samples are implicated.
    0073     %   obj = construct1SampleSoftDistanceConstraint(varargin)  % creates a constraint object for cases where 1 sample is implicated.
    0074     %   cost = initCost() - Calculates, saves, and returns the cost value for the currentitems in the sample.
    0075     %   swCost = swapCost(targSample,targSampleIndex, feederdf,feederdfIndex) - Calculates the new cost if items from targSample and feederdf were swapped.
    0076     %   acceptSwap() - updates variables when the proposed swap is accepted
    0077     %   initGroupMeans() - calculates the means of the samples
    0078     %   initPairedMeans() % prepares data for paired mean calculation
    0079     %   initGroupStdev() %calculates the stdevs of the samples
    0080     %   initPairedStdev() % prepares data for paired stdev calculation
    0081     %   initSingleMeanandTarg()  % calculates the mean of the sample1 and sets the target as the target stat
    0082     %   obj = initSingleStdevandtarg(obj) %calculates the stdevs of the samples
    0083     %   swGroupMeans(targSample,targSampleIndex, feederdf,feederdfIndex) - Calculates the new (swap) means if items from targSample and feederd were swapped.
    0084     %   swPairedMeans(targSample,targSampleIndex, feederdf,feederdfIndex) % Calculates the new (swap) means if items from targSample and  feederd were swapped.
    0085     %   swGroupStdev(targSample,targSampleIndex, feederdf,feederdfIndex) % Calculates the new (swap) stdevs if items from targSample and feederd were swapped.
    0086     %   swPairedStdev(targSample,targSampleIndex, feederdf,feederdfIndex) % Calculates the new (swap) stdev if items from targSample and feederdf were swapped.
    0087     %   swSingleMean(targSample,targSampleIndex, feederdf,feederdfIndex) % Calculates the new (swap) mean if items from targSample and  feederd were swapped.
    0088     %   swSingleStdev(targSample,targSampleIndex, feederdf,feederdfIndex)  % Calculates the new (swap) stdevs if items from targSample and feederd were swapped.
    0089     %   cost = minDiff(x1,x2) -  cost function minimized by reduced differences on the statistic
    0090     %   cost = maxDiff(x1,x2) - cost function minimized by maximizing differences on the stat.  Many uses may want to use orderedMaxDiff, instead.
    0091     %   cost = orderedMaxDiff(x1,x2) - cost function minimized by maximizing a difference between the two groups that adheres to s1.stat < s2.stat cost function minimized by reduced differences on the statistic for paired groups
    0092     %   acceptSwapMeans() % updates variables when the proposed swap is accepted for  means
    0093     %   acceptSwapStdev()   % updates variables when the proposed swap is accepted for standard deviations
    0094     %   cost = acceptSwap() % generic reject function
    0095     %   rejectSwapMeans() % updates variables when swap is rejected
    0096     %   rejectSwapStdev()  % updates variables when the proposed swap is rejected
    0097     %
    0098     %METHODS (Static)
    0099     %   p = softDistanceConstraintInputParser2Sample() generates an input parser with parameter / value pairs and validators for the constructor args
    0100     %
    0101     %METHODS (Static, Access = private)
    0102     %   p = parse2SampleConstructorArgs(varargin) - parses the constructor args for cases invovling 2 samples
    0103     %   p = parse1SampleConstructorArgs(varargin)  % parses the constructor args for cases invovling 1 sample
    0104
    0105     %% PROPERTIES
    0106     properties
    0107         stat % the statistic to calculate
    0108         paired % logical indicating if paired differences or group differences are bieng used
    0109         comparison % handle to specific comparison calculator
    0110         initStats % handle to global stat initialization method
    0111         swStats % handle to local stat update method
    0112         acceptsw % handle to local accept swap method
    0113         rejectsw % handle to local reject swap method
    0114         s1 % sample1
    0115         s2 % sample2
    0116         s1Col %s1 column index
    0117         s2Col %s2 column index
    0118         s1ColName % name of column in s1
    0119         s2ColName % name of column in s2
    0120         stat1 % current stat of sample 1
    0121         stat2 % current stat of sample2
    0122         swstat1 % stat of sample 1 if swap occurs
    0123         swstat2 % stat of sample2 if swap occurs
    0124         sumx1sq % sum of the squared values, x, in an array.  Used in stdev calculation
    0125         meanx1 % square of the mean value of x in an array.  Used in stdev calculation
    0126         swpsumx1sq % swap sum of the squared values, x, in an array.  Used in stdev calculation
    0127         swpmeanx1 % swap square of the mean value of x in an array.  Used in stdev calculation
    0128         sumx2sq % sum of the squared values, x, in an array.  Used in stdev calculation
    0129         meanx2 % square of the mean value of x in an array.  Used in stdev calculation
    0130         swpsumx2sq % swap sum of the squared values, x, in an array.  Used in stdev calculation
    0131         swpmeanx2 % swap square of the mean value of x in an array.  Used in stdev calculation
    0132         targVal % target value to match for 1 sample-to-value matching
    0133         zTargVal % normalized target value
    0134     end
    0135
    0136     methods
    0137
    0138         %% softDistanceConstraint CONSTRUCTOR
    0139         function  obj = softDistanceConstraint(varargin)
    0140             % Creates a softDistance Constraint object.  See the
    0141             % constructors for the 2Sample and 1Sample case for additional
    0142             % parameters
    0143
    0144
    0145             %first, decide what type of object to create.  This will be
    0146             %based on whether the desired function involves 2 means, or 1.
    0147             p = inputParser;
    0148             p.addParamValue('fnc','null', ...
    0149                  @(fnc)any(strcmp({'min', 'max','orderedMax','match1SampleVal'},fnc)));
    0150             p.KeepUnmatched = true;
    0151             p.parse(varargin{:});
    0152
    0153             %cases involving 2 samples
    0154             if any([strcmp(p.Results.fnc,'min'), ...
    0155                     strcmp(p.Results.fnc,'max'), ...
    0156                     strcmp(p.Results.fnc,'orderedMax')])
    0157                 obj = construct2SampleSoftDistanceConstraint(obj,varargin);
    0158             % cases invovling 1 sample matched to a value
    0159             elseif any(strcmp(p.Results.fnc,'match1SampleVal'))
    0160                 obj = construct1SampleSoftDistanceConstraint(obj,varargin);
    0161             else
    0162                 error(['Could not create a soft constraint with <fnc>: ', ...
    0163                         p.Results.fnc]);
    0164             end
    0165
    0166             obj.cost = NaN;
    0167             obj.swCost = NaN;
    0168
    0169
    0170             verbosePrint('Soft Distance Constraint has been created', ...
    0171                     'softDistanceConstraint_Constructor_endObjCreation');
    0172         end % constructor
    0173
    0174         %% obj = construct2SampleSoftDistanceConstraint(varargin) METHOD
    0175         function obj = construct2SampleSoftDistanceConstraint(obj,varargin)
    0176             % creates a constraint object for cases where 2 samples are implicated.
    0177             % CONSTRUCTOR - Creates a softDistanceConstraint object
    0178             %
    0179             % CALL:
    0180             % softDistanceConstraint(varargin<defined below>)
    0181             %
    0182             % PARAMETERS:
    0183             % REQUIRED:
    0184             %   'sosObj'/sos object - the SOS object the constraint will be linked to, and which contains the samples the constraint operates on.
    0185             %   'constraintType'/'soft' - the type of contraint - must be 'soft'
    0186             %   'fnc'/'min'|'max'|'orderedMax' the distance function to create.  If orderedMax, s1 < s2 on the specified dimension
    0187             %   'stat'/'mean'|'stdev' - the statistic to calculate the difference on
    0188             %   'sample1'/sample - the first sample
    0189             %   'sample2'/sample - the second sample
    0190             %   's1ColName'/string - name of column in 1st sample
    0191             %   's2ColName'/string - name of column in 2nd sample
    0192             %   'paired'/logical - should pairwise or group level distances be calculated
    0193             % OPTIONAL:
    0194             %   'exponent'/numeric - defaults to 2 (quadratic difference)
    0195             %   'weight'/numeric - defaults to 1 (equal weighting of all soft costs)
    0196             %
    0197             % EXAMPLE:
    0198             % mySOS.addConstraint('sosObj',mySOS,'constraintType','soft', ...
    0199             %   'fnc','min','stat','mean','sample1',s1,'sample2',s2,'s1ColName','Lg10WF', ...
    0200             %   's2ColName','Lg10WF','exponent',2,'paired',false,'weight', 1);
    0201
    0202
    0203             p = softDistanceConstraint.parse2SampleConstructorArgs(varargin{:});
    0204
    0205             if(p.Results.sosObj.containsSample(p.Results.sample1) == false)
    0206                 error('Cannot create soft distance constraint: sos Object does not contain the sample1');
    0207             end
    0208
    0209             if(p.Results.sosObj.containsSample(p.Results.sample2) == false)
    0210                 error('Cannot create soft distance constraint: sos Object does not contain sample2');
    0211             end
    0212
    0213
    0214             col1 = p.Results.sample1.colName2colNum(p.Results.s1ColName);
    0215             if(col1 == -1)
    0216                 error('Specified column name not found in sample1');
    0217             end
    0218
    0219             if(strcmp(p.Results.sample1.format{col1},'%f') == 0)
    0220                 error('Specified column is not of numeric (%f) format, so cannot use as hard bound');
    0221             end
    0222
    0223             col2 = p.Results.sample2.colName2colNum(p.Results.s2ColName);
    0224             if(col2 == -1)
    0225                 error('Specified column name not found in sample');
    0226             end
    0227
    0228             if(strcmp(p.Results.sample2.format{col2},'%f') == 0)
    0229                 error('Specified column is not of numeric (%f) format, so cannot use as hard bound; 2nd sample');
    0230             end
    0231
    0232             %if the lengths are not going to be identical, do not run the
    0233             %test if it is paired.  The init and swap methods will ensure
    0234             %that if practically the data are NaN, the optimization will
    0235             %stop.
    0236             if(p.Results.paired == true)
    0237                 if length(p.Results.sample1.n) ~= length(p.Results.sample2.n)
    0238                     error('Sample sizes must be equal if using paired matching');
    0239                 end
    0240             end
    0241             % Assign the comparison function appropriate for the constraint
    0242
    0243             if(p.Results.paired ~= true & p.Results.paired ~= false)
    0244                 error('<paired> parameter must be either true/false (1/0)');
    0245             end
    0246
    0247
    0248             %ASSIGN HANDLE TO STAT CALCULATION METHOD
    0249             if(strcmp(p.Results.stat,'mean') && p.Results.paired == false)
    0250                 obj.initStats = @obj.initGroupMeans;
    0251                 obj.swStats = @obj.swGroupMeans;
    0252                 obj.acceptsw = @obj.acceptSwapMeans;
    0253                 obj.rejectsw = @obj.rejectSwapMeans;
    0254             elseif(strcmp(p.Results.stat,'mean') && p.Results.paired == true)
    0255                 obj.initStats = @obj.initPairedMeans;
    0256                 obj.swStats = @obj.swPairedMeans;
    0257                 obj.acceptsw = @obj.acceptSwapMeans;
    0258                 obj.rejectsw = @obj.rejectSwapMeans;
    0259             elseif(strcmp(p.Results.stat,'stdev')  && p.Results.paired == false)
    0260                 obj.initStats = @obj.initGroupStdev;
    0261                 obj.swStats = @obj.swGroupStdev;
    0262                 obj.acceptsw = @obj.acceptSwapStdev;
    0263                 obj.rejectsw = @obj.rejectSwapStdev;
    0264             elseif(strcmp(p.Results.stat,'stdev')  && p.Results.paired == true)
    0265                 obj.initStats = @obj.initPairedStdev;
    0266                 obj.swStats = @obj.swPairedStdev;
    0267                 obj.acceptsw = @obj.acceptSwapStdev;
    0268                 obj.rejectsw = @obj.rejectSwapStdev;
    0269             else
    0270                error('function not yet supported');
    0271             end
    0272
    0273             % ASSIGN HANDLE TO DIFFERENCE COMPARISON METHOD
    0274             % Group differences:
    0275             if(strcmp(p.Results.fnc,'min'))
    0276                 obj.comparison = @obj.minDiff;
    0277             elseif(strcmp(p.Results.fnc,'max'))
    0278                 obj.comparison = @obj.maxDiff;
    0279             elseif(strcmp(p.Results.fnc,'orderedMax'))
    0280                 obj.comparison = @obj.orderedMaxDiff;
    0281             else
    0282                 error('function not yet supported');
    0283             end
    0284
    0285             % parent properties
    0286             obj.sosObj = p.Results.sosObj;
    0287             obj.constraintType = p.Results.constraintType;
    0288             obj.fnc = p.Results.fnc;
    0289
    0290
    0291             obj.weight = p.Results.weight;
    0292             obj.exp = p.Results.exponent;
    0293
    0294             obj.stat = p.Results.stat;
    0295             obj.s1 = p.Results.sample1;
    0296             obj.s2 = p.Results.sample2;
    0297             obj.s1Col = col1;
    0298             obj.s2Col = col2;
    0299             obj.s1ColName = p.Results.s1ColName;
    0300             obj.s2ColName = p.Results.s2ColName;
    0301
    0302             obj.paired = p.Results.paired;
    0303
    0304
    0305             % add the name and the label
    0306             obj.label = [obj.constraintType,'_',obj.fnc,'_',...
    0307                     obj.stat,'_',...
    0308                     obj.s1.name,'_',obj.s1ColName,'_',...
    0309                     obj.s2.name,'_',obj.s2ColName,'_',...
    0310                     'p',num2str(obj.paired)','_w',...
    0311                     num2str(obj.weight),'_e',num2str(obj.exp)];
    0312             if any(strcmp(p.UsingDefaults,'name'))
    0313                 obj.name = obj.label;
    0314             else
    0315                  obj.name = p.Results.name;
    0316             end
    0317         end %construct2SampleSoftDistanceConstraint
    0318
    0319         %% obj = construct1SampleSoftDistanceConstraint(varargin) METHOD
    0320         function obj = construct1SampleSoftDistanceConstraint(obj,varargin)
    0321             % creates a constraint object for cases where 1 sample is implicated.
    0322             % CONSTRUCTOR - Creates a softDistanceConstraint object
    0323             %
    0324             % CALL:
    0325             % softDistanceConstraint(varargin<defined below>)
    0326             %
    0327             % PARAMETERS:
    0328             % REQUIRED:
    0329             %   'sosObj'/sos object - the SOS object the constraint will be linked to, and which contains the samples the constraint operates on.
    0330             %   'constraintType'/'soft' - the type of contraint - must be 'soft'
    0331             %   'fnc'/'match1SampleVal' the distance function to create.  If orderedMax, s1 < s2 on the specified dimension
    0332             %   'stat'/'mean'|'stdev' - the statistic to calculate the difference on
    0333             %   'sample1'/sample - the first sample
    0334             %   's1ColName'/string - name of column in 1st sample
    0335             % OPTIONAL:
    0336             %   'exponent'/numeric - defaults to 2 (quadratic difference)
    0337             %   'weight'/numeric - defaults to 1 (equal weighting of all soft costs)
    0338             %   'targVal'/numeric - value to match the stat to.
    0339             %
    0340
    0341
    0342             p = softDistanceConstraint.parse1SampleConstructorArgs(varargin{:});
    0343
    0344             if(p.Results.sosObj.containsSample(p.Results.sample1) == false)
    0345                 error('Cannot create soft distance constraint: sos Object does not contain the sample1');
    0346             end
    0347
    0348
    0349             col1 = p.Results.sample1.colName2colNum(p.Results.s1ColName);
    0350             if(col1 == -1)
    0351                 error('Specified column name not found in sample1');
    0352             end
    0353
    0354             if(strcmp(p.Results.sample1.format{col1},'%f') == 0)
    0355                 error('Specified column is not of numeric (%f) format, so cannot use as hard bound');
    0356             end
    0357
    0358
    0359             % Assign the comparison function appropriate for the constraint
    0360
    0361             %ASSIGN HANDLE TO STAT CALCULATION METHOD
    0362             if(strcmp(p.Results.stat,'mean'))
    0363                 obj.initStats = @obj.initSingleMeanandTarg;
    0364                 obj.swStats = @obj.swSingleMean;
    0365                 obj.acceptsw = @obj.acceptSwapMeans;
    0366                 obj.rejectsw = @obj.rejectSwapMeans;
    0367
    0368                 obj.comparison = @obj.minDiff;
    0369
    0370             elseif(strcmp(p.Results.stat,'stdev'))
    0371                 obj.initStats = @obj.initSingleStdevandTarg;
    0372                 obj.swStats = @obj.swSingleStdev;
    0373                 obj.acceptsw = @obj.acceptSwapStdev;
    0374                 obj.rejectsw = @obj.rejectSwapStdev;
    0375
    0376                 obj.comparison = @obj.minDiff;
    0377             else
    0378                error('function not yet supported');
    0379             end
    0380
    0381
    0382             %Finally, need to convert the raw value supplied by the user
    0383             %into the standardized value used for comparison purposes.  We
    0384             %do this by conslulting the SOS object's normalization data.
    0385
    0386             %find the normalization params for this data column:
    0387
    0388             obj.targVal = p.Results.targVal;
    0389             obj.zTargVal = NaN;
    0390
    0391             % parent properties
    0392             obj.sosObj = p.Results.sosObj;
    0393             obj.constraintType = p.Results.constraintType;
    0394             obj.fnc = p.Results.fnc;
    0395
    0396
    0397             obj.weight = p.Results.weight;
    0398
    0399
    0400             obj.stat = p.Results.stat;
    0401             obj.s1 = p.Results.sample1;
    0402             obj.s1Col = col1;
    0403             obj.s2 = NaN;
    0404             obj.s1ColName = p.Results.s1ColName;
    0405             obj.exp = p.Results.exponent;
    0406
    0407             % add the name and the label
    0408             obj.label = [obj.constraintType,'_',obj.fnc,'_',...
    0409                     obj.stat,'_',num2str(obj.targVal),'_',obj.s1.name,'_',...
    0410                     obj.s1ColName,'_w',...
    0411                     num2str(obj.weight),'_e',num2str(obj.exp)];
    0412             if any(strcmp(p.UsingDefaults,'name'))
    0413                 obj.name = obj.label;
    0414             else
    0415                  obj.name = p.Results.name;
    0416             end
    0417
    0418         end %construct1SampleSoftDistanceConstraint
    0419
    0420
    0421
    0422         %% cost = initCost() METHOD
    0423         function cost = initCost(obj)
    0424             % Calculates, saves, and returns the cost value for the current items in the sample.
    0425             %
    0426             %CALL:
    0427             %   <softDistanceConstraint>.initCost();
    0428             %
    0429             %SYNOPSIS:
    0430             % Calculates the cost for the current items in the sample based
    0431             % on the specified constraint.  This calculation is done on the
    0432             % global dataset, rather than using the differential local
    0433             % computation used when calculating swap costs.
    0434             %
    0435
    0436             %init the stats, then compare them.  Return the calculated cost
    0437             obj = obj.initStats();
    0438             cost = obj.comparison(obj.stat1,obj.stat2);
    0439
    0440             obj.swstat1 = NaN;
    0441             obj.swstat2 = NaN;
    0442
    0443             obj.cost = cost;
    0444             obj.swCost = NaN;
    0445         end
    0446
    0447         %%  swCost(targSample,targSampleIndex, feederdf,feederdfIndex) FUNCTION
    0448         function swCost = swapCost(obj,targSample,targSampleIndex, feederdf,feederdfIndex)
    0449             % Calculates the new cost if items from targSample and feederdf were swapped.
    0450             %
    0451             %By definition, if this method is called it means that at least
    0452             % one of the two swap objects is implicated in this function
    0453             %
    0454             %PARAMETERS:
    0455             %   targSample - the target sample (i.e., the object that will call it's swapSample() method if a swap later occurs).
    0456             %   targSampleIndex - row index of item to swap
    0457             %   feederdf - dataframe (sample/pop) containin the other item to swap
    0458             %   feederdfIndex - row index of item to swap.
    0459
    0460            if (obj.s1 ~= targSample && obj.s2 ~= targSample ...
    0461                    && obj.s1 ~= feederdf && obj.s2  ~= feederdf)
    0462                error('swCost called, but no sample part of this cost function');
    0463            end
    0464
    0465            % update the stats, then calculate cost of new stats
    0466            obj.swStats(targSample,targSampleIndex, feederdf,feederdfIndex);
    0467
    0468             swCost = obj.comparison(obj.swstat1,obj.swstat2);
    0469             obj.swCost = swCost;
    0470
    0471         end
    0472
    0473
    0474         %% initGroupMeans() METHOD
    0475         function obj = initGroupMeans(obj)
    0476             % calculates the means of the samples
    0477             obj.stat1= mean(obj.s1.zdata{obj.s1Col});
    0478             obj.stat2= mean(obj.s2.zdata{obj.s2Col});
    0479
    0480
    0481             if isnan(obj.stat1) || isnan(obj.stat2)
    0482                 error('NaN obtained during initstat computation (perhaps there is missing data for an item?)');
    0483             end
    0484         end
    0485
    0486         %% initPairedMeans() METHOD
    0487         function obj = initPairedMeans(obj)
    0488             % prepares data for paired mean calculation
    0489
    0490             % make sure there are no nan's.
    0491             if (any(isnan(obj.s1.zdata{obj.s1Col})))
    0492                 error('NaN obtained during initstat computation of sample 1 (perhaps there is missing data for an item?)');
    0493             elseif (any(isnan(obj.s2.zdata{obj.s2Col})))
    0494                 error('NaN obtained during initstat computation of sample 2 (perhaps there is missing data for an item?)');
    0495             end
    0496
    0497             % calculate the statistic.  In contrast to the group mean
    0498             % statistic, here we'll calculate the difference in each pair
    0499             % of observations and sum that in stat2, and leave stat1  ==0
    0500
    0501             tmpstat2 = 0;
    0502             for i=1:obj.s1.n
    0503                 x = obj.s2.zdata{obj.s2Col}(i) - obj.s1.zdata{obj.s1Col}(i);
    0504
    0505                 % when trying to minimize, any difference, in either
    0506                 % direction, is bad.  When trying to maximize, then let the
    0507                 % sign enter into the equation.
    0508                 if strcmp(obj.fnc,'min')
    0509                     x = abs(x);
    0510                 end
    0511
    0512                  tmpstat2 = tmpstat2 + x;
    0513             end
    0514
    0515             % calculate the mean of x
    0516             obj.stat2 = tmpstat2/obj.s1.n;
    0517             obj.stat1 = 0; % always relative to no difference
    0518
    0519
    0520         end
    0521
    0522         %% initStdev(obj) METHOD
    0523         function obj = initGroupStdev(obj)
    0524             %calculates the stdevs of the samples
    0525
    0526             obj.sumx1sq = sum((obj.s1.zdata{obj.s1Col}).^2);
    0527             obj.meanx1 = mean(obj.s1.zdata{obj.s1Col});
    0528             n1 = length(obj.s1.zdata{obj.s1Col});
    0529
    0530             %use the computational formula to efficiently calculate the sum
    0531             stdevx1 = (1/(n1-1) * obj.sumx1sq ...
    0532                         - n1/(n1-1) * obj.meanx1^2)^0.5;
    0533
    0534             obj.sumx2sq = sum((obj.s2.zdata{obj.s2Col}).^2);
    0535             obj.meanx2 = mean(obj.s2.zdata{obj.s2Col});
    0536             n2 = length(obj.s2.zdata{obj.s2Col});
    0537
    0538             %use the computational formula to efficiently calculate the sum
    0539             stdevx2 = (1/(n2-1) * obj.sumx2sq ...
    0540                         - n2/(n2-1) * obj.meanx2^2)^0.5;
    0541
    0542             obj.stat1 = stdevx1;
    0543             obj.stat2 = stdevx2;
    0544
    0545             if isnan(obj.stat1) || isnan(obj.stat2)
    0546                 error('NaN obtained during initstat computation (perhaps there is missing data for an item?)');
    0547             end
    0548
    0549         end
    0550
    0551         %% initPairedMeans() METHOD
    0552         function obj = initPairedStdev(obj)
    0553             % prepares data for paired mean calculation
    0554
    0555             % make sure there are no nan's.
    0556             if (any(isnan(obj.s1.zdata{obj.s1Col})))
    0557                 error('NaN obtained during initstat computation of sample 1 (perhaps there is missing data for an item?)');
    0558             elseif (any(isnan(obj.s2.zdata{obj.s2Col})))
    0559                 error('NaN obtained during initstat computation of sample 2 (perhaps there is missing data for an item?)');
    0560             end
    0561
    0562             % calculate the statistic.  In contrast to the group mean
    0563             % statistic, here we'll calculate the difference in each pair
    0564             % of observations and sum that in stat2, and leave stat1  ==0
    0565
    0566             obj.sumx2sq = 0;
    0567             obj.meanx2 = 0;
    0568             n = length(obj.s2.zdata{obj.s2Col});
    0569
    0570             for i=1:n
    0571                 x = obj.s2.zdata{obj.s2Col}(i) - obj.s1.zdata{obj.s1Col}(i);
    0572                 obj.sumx2sq = obj.sumx2sq + x^2;
    0573                 obj.meanx2 = obj.meanx2 + x/n;
    0574
    0575             end
    0576
    0577             %use the computational formula to efficiently calculate the sum
    0578             stdevx2 = (1/(n-1) * obj.sumx2sq ...
    0579                         - n/(n-1) * obj.meanx2^2)^0.5;
    0580
    0581
    0582             % calculate the mean of x
    0583             obj.stat2 = stdevx2;
    0584             obj.stat1 = 0; % always relative to no difference
    0585
    0586
    0587         end
    0588
    0589         %% initSingleMeanandTarg() METHOD
    0590         function obj = initSingleMeanandTarg(obj)
    0591             % calculates the mean of the sample1 and sets the target as the target stat
    0592
    0593             obj.stat2= mean(obj.s1.zdata{obj.s1Col});
    0594
    0595              %normalize the target value to match against
    0596             targCol = obj.sosObj.allData.colName2colNum(obj.s1ColName);
    0597             if(targCol == -1)
    0598                 error('Normalized data for this value may not yet exist (try normalizing first)');
    0599             end
    0600
    0601             obj.zTargVal = (obj.targVal - obj.sosObj.allDataColMean(targCol)) ...
    0602                             / obj.sosObj.allDataColStd(targCol);
    0603
    0604            obj.stat1 = obj.zTargVal;
    0605
    0606             if isnan(obj.stat1) || isnan(obj.stat2)
    0607                 error('NaN obtained during initstat computation (perhaps there is missing data for an item?)');
    0608             end
    0609         end
    0610
    0611         %% initSingleStdevandtarg(obj) METHOD
    0612         function obj = initSingleStdevandTarg(obj)
    0613             %calculates the stdevs of the samples
    0614
    0615             obj.sumx2sq = sum((obj.s1.zdata{obj.s1Col}).^2);
    0616             obj.meanx2 = mean(obj.s1.zdata{obj.s1Col});
    0617             n2 = length(obj.s1.zdata{obj.s1Col});
    0618
    0619             %use the computational formula to efficiently calculate the sum
    0620             stdevx2 = (1/(n2-1) * obj.sumx2sq ...
    0621                         - n2/(n2-1) * obj.meanx2^2)^0.5;
    0622
    0623             obj.stat2 = stdevx2;
    0624
    0625              %normalize the target value to match against
    0626             targCol = obj.sosObj.allData.colName2colNum(obj.s1ColName);
    0627             if(targCol == -1)
    0628                 error('Normalized data for this value may not yet exist (try normalizing first)');
    0629             end
    0630
    0631             obj.zTargVal = (obj.targVal) ...
    0632                             / obj.sosObj.allDataColStd(targCol);
    0633
    0634             obj.stat1 = obj.zTargVal;
    0635
    0636
    0637             if isnan(obj.stat1) || isnan(obj.stat2)
    0638                 error('NaN obtained during initstat computation (perhaps there is missing data for an item?)');
    0639             end
    0640
    0641         end
    0642
    0643         %% swGroupMeans(targSample,targSampleIndex,feederdf,feederdfIndex) FUNCTION
    0644         function swGroupMeans(obj,targSample,targSampleIndex, feederdf,feederdfIndex)
    0645            % Calculates the new (swap) means if items from targSample and feederd were swapped.
    0646            %
    0647            % Inputs are the same as for swapCost()
    0648
    0649            tempswm1 = obj.stat1;
    0650            tempswm2 = obj.stat2;
    0651
    0652             %do the adjustments of the means
    0653             %basic idea is to take out the old value from that mean and
    0654             %then put in the new one.  Do that seperately for each
    0655             %dataframe to cover all eventualities (i.e., if either
    0656             %targSample or feederdf, or both, are part of the current cost
    0657             %function.
    0658
    0659             if targSample == obj.s1
    0660                 tempswm1 = tempswm1 - (targSample.zdata{obj.s1Col}(targSampleIndex)/length(targSample.zdata{obj.s1Col}));
    0661                 tempswm1 = tempswm1 + (feederdf.zdata{obj.s1Col}(feederdfIndex)/length(targSample.zdata{obj.s1Col}));
    0662             elseif targSample == obj.s2
    0663                 tempswm2 = tempswm2 - (targSample.zdata{obj.s2Col}(targSampleIndex)/length(targSample.zdata{obj.s2Col}));
    0664                 tempswm2 = tempswm2 + (feederdf.zdata{obj.s2Col}(feederdfIndex)/length(targSample.zdata{obj.s2Col}));
    0665             end
    0666
    0667             if feederdf == obj.s1
    0668                 tempswm1 = tempswm1 - (feederdf.zdata{obj.s1Col}(feederdfIndex)/length(feederdf.zdata{obj.s1Col}));
    0669                 tempswm1 = tempswm1 + (targSample.zdata{obj.s1Col}(targSampleIndex)/length(feederdf.zdata{obj.s1Col}));
    0670             elseif feederdf == obj.s2
    0671                 tempswm2 = tempswm2 - (feederdf.zdata{obj.s2Col}(feederdfIndex)/length(feederdf.zdata{obj.s2Col}));
    0672                 tempswm2 = tempswm2 + (targSample.zdata{obj.s2Col}(targSampleIndex)/length(feederdf.zdata{obj.s2Col}));
    0673             end
    0674
    0675             obj.swstat1 = tempswm1;
    0676             obj.swstat2 = tempswm2;
    0677
    0678             if isnan(obj.swstat1) || isnan(obj.swstat2)
    0679                 error('NaN obtained during swstat computation (perhaps there is missing data for an item?)');
    0680             end
    0681         end
    0682
    0683
    0684
    0685
    0686         %% swPairedMeans(targSample,targSampleIndex,feederdf,feederdfIndex) FUNCTION
    0687         function swPairedMeans(obj,targSample,targSampleIndex, feederdf,feederdfIndex)
    0688            % Calculates the new (swap) means if items from targSample and feederd were swapped.
    0689            %
    0690            % Inputs are the same as for swapCost()
    0691
    0692            tempswm2 = obj.stat2;
    0693            %tempswm2 should remain 0 at all times
    0694
    0695             %do the adjustments of the means
    0696             %basic idea is to take out the old value from that mean and
    0697             %then put in the new one.  Do that seperately for each
    0698             %dataframe to cover all eventualities (i.e., if either
    0699             %targSample or feederdf, or both, are part of the current cost
    0700             %function.
    0701
    0702
    0703             nobs = length(obj.s1.zdata{obj.s1Col});
    0704
    0705             if targSample == obj.s1
    0706                 %take the existing contribution to the stat due to the data
    0707                 %out.
    0708                 curx = (obj.s2.zdata{obj.s2Col}(targSampleIndex) - ...
    0709                         targSample.zdata{obj.s1Col}(targSampleIndex))/nobs;
    0710
    0711                 if strcmp(obj.fnc,'min')
    0712                     curx = abs(curx);
    0713                 end
    0714
    0715                 tempswm2 = tempswm2 - curx;
    0716
    0717                 %add in the new value
    0718                 newx = (obj.s2.zdata{obj.s2Col}(targSampleIndex) - ...
    0719                           feederdf.zdata{obj.s1Col}(feederdfIndex)) / nobs;
    0720
    0721                 if strcmp(obj.fnc,'min')
    0722                     newx = abs(newx);
    0723                 end
    0724
    0725                 tempswm2 = tempswm2 + newx;
    0726
    0727                 % update is complete
    0728             elseif targSample == obj.s2
    0729
    0730                 curx  = (targSample.zdata{obj.s2Col}(targSampleIndex) - ...
    0731                             obj.s1.zdata{obj.s1Col}(targSampleIndex))/nobs;
    0732
    0733                 if strcmp(obj.fnc,'min')
    0734                     curx = abs(curx);
    0735                 end
    0736
    0737                 tempswm2 = tempswm2 - curx;
    0738
    0739                 newx = (feederdf.zdata{obj.s2Col}(feederdfIndex) - ...
    0740                         obj.s1.zdata{obj.s1Col}(targSampleIndex))/nobs;
    0741
    0742                 if strcmp(obj.fnc,'min')
    0743                     newx = abs(newx);
    0744                 end
    0745
    0746                 tempswm2 = tempswm2 + newx;
    0747
    0748             end
    0749
    0750
    0751             if feederdf == obj.s1
    0752
    0753                 curx = (obj.s2.zdata{obj.s2Col}(feederdfIndex) - ...
    0754                           feederdf.zdata{obj.s1Col}(feederdfIndex)) / nobs;
    0755
    0756                 if strcmp(obj.fnc,'min')
    0757                     curx = abs(curx);
    0758                 end
    0759
    0760                 tempswm2 = tempswm2 - curx;
    0761
    0762                 newx = (obj.s2.zdata{obj.s2Col}(feederdfIndex) - ...
    0763                         targSample.zdata{obj.s1Col}(targSampleIndex))/nobs;
    0764
    0765                  if strcmp(obj.fnc,'min')
    0766                     newx = abs(newx);
    0767                 end
    0768
    0769                 tempswm2 = tempswm2 + newx;
    0770
    0771
    0772             elseif feederdf == obj.s2
    0773                 curx = (feederdf.zdata{obj.s2Col}(feederdfIndex) - ...
    0774                         obj.s1.zdata{obj.s1Col}(feederdfIndex))/nobs;
    0775
    0776
    0777                 if strcmp(obj.fnc,'min')
    0778                     curx = abs(curx);
    0779                 end
    0780
    0781                 tempswm2 = tempswm2 - curx;
    0782
    0783                 newx = (targSample.zdata{obj.s2Col}(targSampleIndex) - ...
    0784                             obj.s1.zdata{obj.s1Col}(feederdfIndex))/nobs;
    0785
    0786                 if strcmp(obj.fnc,'min')
    0787                     newx = abs(newx);
    0788                 end
    0789
    0790                 tempswm2 = tempswm2 + newx;
    0791
    0792             end
    0793
    0794             obj.swstat2 = tempswm2;
    0795             obj.swstat1 = obj.stat1; % again, just copy the zero over
    0796
    0797             if isnan(obj.swstat1)
    0798                 error('NaN obtained during swstat computation (perhaps there is missing data for an item?)');
    0799             end
    0800         end %swPairedMeans
    0801
    0802         %% swGroupStdev(targSample,targSampleIndex, feederdf,feederdfIndex)
    0803         function swGroupStdev(obj,targSample,targSampleIndex, feederdf,feederdfIndex)
    0804            % Calculates the new (swap) stdevs if items from targSample and feederd were swapped.
    0805            %
    0806            % Inputs are the same as for swapCost()
    0807
    0808             n1 = length(obj.s1.zdata{obj.s1Col});
    0809             n2 = length(obj.s2.zdata{obj.s2Col});
    0810
    0811             obj.swpsumx1sq = obj.sumx1sq;
    0812             obj.swpmeanx1 = obj.meanx1;
    0813
    0814             obj.swpsumx2sq = obj.sumx2sq;
    0815             obj.swpmeanx2 = obj.meanx2;
    0816
    0817             if targSample == obj.s1
    0818                 %must update 2 params %first, take out the old observation, then add in thew new
    0819                 obj.swpsumx1sq = obj.swpsumx1sq - (targSample.zdata{obj.s1Col}(targSampleIndex))^2;
    0820                 obj.swpmeanx1 = obj.swpmeanx1 - (targSample.zdata{obj.s1Col}(targSampleIndex))/n1;
    0821                 obj.swpsumx1sq = obj.swpsumx1sq + (feederdf.zdata{obj.s1Col}(feederdfIndex))^2;
    0822                 obj.swpmeanx1 = obj.swpmeanx1 + (feederdf.zdata{obj.s1Col}(feederdfIndex))/n1;
    0823             elseif targSample == obj.s2
    0824                 obj.swpsumx2sq = obj.swpsumx2sq - (targSample.zdata{obj.s2Col}(targSampleIndex))^2;
    0825                 obj.swpmeanx2 = obj.swpmeanx2 - (targSample.zdata{obj.s2Col}(targSampleIndex))/n2;
    0826                 obj.swpsumx2sq = obj.swpsumx2sq + (feederdf.zdata{obj.s2Col}(feederdfIndex))^2;
    0827                 obj.swpmeanx2 = obj.swpmeanx2 + (feederdf.zdata{obj.s2Col}(feederdfIndex))/n2;
    0828             end
    0829
    0830             if feederdf == obj.s1
    0831                 obj.swpsumx1sq = obj.swpsumx1sq -  (feederdf.zdata{obj.s1Col}(feederdfIndex))^2;
    0832                 obj.swpmeanx1 = obj.swpmeanx1 - (feederdf.zdata{obj.s1Col}(feederdfIndex))/n1;
    0833                 obj.swpsumx1sq = obj.swpsumx1sq + (targSample.zdata{obj.s1Col}(targSampleIndex))^2;
    0834                 obj.swpmeanx1 = obj.swpmeanx1 + (targSample.zdata{obj.s1Col}(targSampleIndex))/n1;
    0835             elseif feederdf == obj.s2
    0836                 obj.swpsumx2sq = obj.swpsumx2sq -  (feederdf.zdata{obj.s2Col}(feederdfIndex))^2;
    0837                 obj.swpmeanx2 = obj.swpmeanx2 - (feederdf.zdata{obj.s2Col}(feederdfIndex))/n2;
    0838                 obj.swpsumx2sq = obj.swpsumx2sq + (targSample.zdata{obj.s2Col}(targSampleIndex))^2;
    0839                 obj.swpmeanx2 = obj.swpmeanx2 + (targSample.zdata{obj.s2Col}(targSampleIndex))/n2;
    0840             end
    0841
    0842             stdevx1 = (1/(n1-1) * obj.swpsumx1sq ...
    0843                         - n1/(n1-1) * obj.swpmeanx1^2)^0.5;
    0844
    0845             stdevx2 = (1/(n2-1) * obj.swpsumx2sq ...
    0846                         - n2/(n2-1) * obj.swpmeanx2^2)^0.5;
    0847
    0848             obj.swstat1 = stdevx1;
    0849             obj.swstat2 = stdevx2;
    0850
    0851             if isnan(obj.swstat1) || isnan(obj.swstat2)
    0852                 error('NaN obtained during swstat computation (perhaps there is missing data for an item?)');
    0853             end
    0854
    0855         end
    0856
    0857         %% swPairedStdev(targSample,targSampleIndex,feederdf,feederdfIndex) FUNCTION
    0858         function swPairedStdev(obj,targSample,targSampleIndex, feederdf,feederdfIndex)
    0859            % Calculates the new (swap) stdev if items from targSample and feederd were swapped.
    0860            %
    0861            %NOTE: When repeated tens of thousands of times, this manner of
    0862            %updated std via local difference calculations tends to lead to
    0863            %some very small imprecisions relative to re-calculating the
    0864            %actual values from scratch.  These deviations appear to be
    0865            %trivially small in all cases examined to date though.  This
    0866            %fact is mentioned here presently only because the
    0867            %detail-oriented individual may find values like  0.00000001
    0868            %instead of 0, which nevertheless
    0869            %likely will have no practical consequence
    0870            %for the matching algorithm.
    0871            %
    0872            % Inputs are the same as for swapCost()
    0873
    0874
    0875
    0876             obj.swpsumx2sq = obj.sumx2sq;
    0877             obj.swpmeanx2 = obj.meanx2;
    0878             n = length(obj.s1.zdata{obj.s1Col});
    0879
    0880             if targSample == obj.s1
    0881                 %take the existing contribution to the stat due to the data
    0882                 %out.
    0883                 curx = (obj.s2.zdata{obj.s2Col}(targSampleIndex) - ...
    0884                         targSample.zdata{obj.s1Col}(targSampleIndex));
    0885
    0886                 obj.swpsumx2sq = obj.swpsumx2sq - curx^2;
    0887                 obj.swpmeanx2 = obj.swpmeanx2 - curx/n;
    0888
    0889                 %add in the new value
    0890                 newx = (obj.s2.zdata{obj.s2Col}(targSampleIndex) - ...
    0891                           feederdf.zdata{obj.s1Col}(feederdfIndex));
    0892
    0893                 obj.swpsumx2sq = obj.swpsumx2sq + newx^2;
    0894                 obj.swpmeanx2 = obj.swpmeanx2 + newx/n;
    0895
    0896                 % update is complete
    0897             elseif targSample == obj.s2
    0898
    0899                 curx  = (targSample.zdata{obj.s2Col}(targSampleIndex) - ...
    0900                             obj.s1.zdata{obj.s1Col}(targSampleIndex));
    0901
    0902                 obj.swpsumx2sq = obj.swpsumx2sq - curx^2;
    0903                 obj.swpmeanx2 = obj.swpmeanx2 - curx/n;
    0904
    0905                 newx = (feederdf.zdata{obj.s2Col}(feederdfIndex) - ...
    0906                         obj.s1.zdata{obj.s1Col}(targSampleIndex));
    0907
    0908                 obj.swpsumx2sq = obj.swpsumx2sq + newx^2;
    0909                 obj.swpmeanx2 = obj.swpmeanx2 + newx/n;
    0910
    0911             end
    0912
    0913
    0914             if feederdf == obj.s1
    0915
    0916                 curx = (obj.s2.zdata{obj.s2Col}(feederdfIndex) - ...
    0917                           feederdf.zdata{obj.s1Col}(feederdfIndex));
    0918
    0919                 obj.swpsumx2sq = obj.swpsumx2sq - curx^2;
    0920                 obj.swpmeanx2 = obj.swpmeanx2 - curx/n;
    0921
    0922                 newx = (obj.s2.zdata{obj.s2Col}(feederdfIndex) - ...
    0923                         targSample.zdata{obj.s1Col}(targSampleIndex));
    0924
    0925                 obj.swpsumx2sq = obj.swpsumx2sq + newx^2;
    0926                 obj.swpmeanx2 = obj.swpmeanx2 + newx/n;
    0927
    0928
    0929             elseif feederdf == obj.s2
    0930                 curx = (feederdf.zdata{obj.s2Col}(feederdfIndex) - ...
    0931                         obj.s1.zdata{obj.s1Col}(feederdfIndex));
    0932
    0933                 obj.swpsumx2sq = obj.swpsumx2sq - curx^2;
    0934                 obj.swpmeanx2 = obj.swpmeanx2 - curx/n;
    0935
    0936                 newx = (targSample.zdata{obj.s2Col}(targSampleIndex) - ...
    0937                             obj.s1.zdata{obj.s1Col}(feederdfIndex));
    0938
    0939                 obj.swpsumx2sq = obj.swpsumx2sq + newx^2;
    0940                 obj.swpmeanx2 = obj.swpmeanx2 + newx/n;
    0941
    0942             end
    0943
    0944             % Special override:
    0945             % if it happens that targSample and feederdf both correspond to
    0946             % the samples for which stdev is being calculated, and that
    0947             % the exact same item is being swapped out in both cases, the
    0948             % swpsumx2sq should not change because the two swaps should
    0949             % cancel each other out.  However, in the current
    0950             % implementation this doesn't happen and instead we get 2x the
    0951             % negative subtraction because the second calculation is not
    0952             % aware of the shifts completed in the first.  This can,
    0953             % however, be fixed in the present case quite simply by not
    0954             % changing swpsumx2sq in this specific instance.  This only
    0955             % happens in this case because the squaring of the terms leads
    0956             % to the sign of the calculation being ignored, and as such the
    0957             % calculation of swap means should not suffer in the same
    0958             % fashion.
    0959             if((targSample == obj.s1 || targSample == obj.s2) && ...
    0960                     (feederdf == obj.s1 || feederdf == obj.s2) && ...
    0961                 targSampleIndex == feederdfIndex)
    0962                 obj.swpsumx2sq = obj.sumx2sq;
    0963             end
    0964
    0965             % calculate the updated stdev:
    0966            %use the computational formula to efficiently calculate the sum
    0967             swpstdevx2 = (1/(n-1) * obj.swpsumx2sq ...
    0968                         - n/(n-1) * obj.swpmeanx2^2)^0.5;
    0969
    0970             obj.swstat2 = swpstdevx2;
    0971             obj.swstat1 = obj.stat1; % again, just copy the zero over
    0972
    0973             if isnan(obj.swstat1)
    0974                 error('NaN obtained during swstat computation (perhaps there is missing data for an item?)');
    0975             end
    0976         end %swPairedStdev
    0977
    0978
    0979         %% swSingleMean(targSample,targSampleIndex,feederdf,feederdfIndex) METHOD
    0980         function swSingleMean(obj,targSample,targSampleIndex, feederdf,feederdfIndex)
    0981            % Calculates the new (swap) mean if items from targSample and feederd were swapped.
    0982            %
    0983            % Inputs are the same as for swapCost()
    0984
    0985            tempswm2 = obj.stat2;
    0986
    0987             %do the adjustments of the means
    0988             %basic idea is to take out the old value from that mean and
    0989             %then put in the new one.  Do that seperately for each
    0990             %dataframe to cover all eventualities (i.e., if either
    0991             %targSample or feederdf, or both, are part of the current cost
    0992             %function.
    0993
    0994             if targSample == obj.s1
    0995                 tempswm2 = tempswm2 - (targSample.zdata{obj.s1Col}(targSampleIndex)/length(targSample.zdata{obj.s1Col}));
    0996                 tempswm2 = tempswm2 + (feederdf.zdata{obj.s1Col}(feederdfIndex)/length(targSample.zdata{obj.s1Col}));
    0997             end
    0998
    0999
    1000             if feederdf == obj.s1
    1001                 tempswm2 = tempswm2 - (feederdf.zdata{obj.s1Col}(feederdfIndex)/length(feederdf.zdata{obj.s1Col}));
    1002                 tempswm2 = tempswm2 + (targSample.zdata{obj.s1Col}(targSampleIndex)/length(feederdf.zdata{obj.s1Col}));
    1003             end
    1004
    1005             obj.swstat2 = tempswm2;
    1006             obj.swstat1 = obj.stat1;
    1007
    1008             if isnan(obj.swstat1) || isnan(obj.swstat2)
    1009                 error('NaN obtained during swstat computation (perhaps there is missing data for an item?)');
    1010             end
    1011         end
    1012
    1013         %% swSingleStdev(targSample,targSampleIndex, feederdf,feederdfIndex)
    1014         function swSingleStdev(obj,targSample,targSampleIndex, feederdf,feederdfIndex)
    1015            % Calculates the new (swap) stdevs if items from targSample and feederd were swapped.
    1016            %
    1017            % Inputs are the same as for swapCost()
    1018
    1019             n2 = length(obj.s1.zdata{obj.s1Col});
    1020
    1021             obj.swpsumx2sq = obj.sumx2sq;
    1022             obj.swpmeanx2 = obj.meanx2;
    1023
    1024             if targSample == obj.s1
    1025                 %must update 2 params %first, take out the old observation, then add in thew new
    1026                 obj.swpsumx2sq = obj.swpsumx2sq - (targSample.zdata{obj.s1Col}(targSampleIndex))^2;
    1027                 obj.swpmeanx2 = obj.swpmeanx2 - (targSample.zdata{obj.s1Col}(targSampleIndex))/n2;
    1028                 obj.swpsumx2sq = obj.swpsumx2sq + (feederdf.zdata{obj.s1Col}(feederdfIndex))^2;
    1029                 obj.swpmeanx2 = obj.swpmeanx2 + (feederdf.zdata{obj.s1Col}(feederdfIndex))/n2;
    1030             end
    1031
    1032             if feederdf == obj.s1
    1033                 obj.swpsumx2sq = obj.swpsumx2sq -  (feederdf.zdata{obj.s1Col}(feederdfIndex))^2;
    1034                 obj.swpmeanx2 = obj.swpmeanx2 - (feederdf.zdata{obj.s1Col}(feederdfIndex))/n2;
    1035                 obj.swpsumx2sq = obj.swpsumx2sq + (targSample.zdata{obj.s1Col}(targSampleIndex))^2;
    1036                 obj.swpmeanx2 = obj.swpmeanx2 + (targSample.zdata{obj.s1Col}(targSampleIndex))/n2;
    1037             end
    1038
    1039             stdevx2 = (1/(n2-1) * obj.swpsumx2sq ...
    1040                         - n2/(n2-1) * obj.swpmeanx2^2)^0.5;
    1041
    1042             obj.swstat1 = obj.stat1; %just copy over the target
    1043             obj.swstat2 = stdevx2;
    1044
    1045             if isnan(obj.swstat1) || isnan(obj.swstat2)
    1046                 error('NaN obtained during swstat computation (perhaps there is missing data for an item?)');
    1047             end
    1048
    1049         end
    1050
    1051
    1052
    1053         %% minDiff() FUNCTION
    1054         function cost = minDiff(obj,x1,x2)
    1055             % cost function minimized by reduced differences on the statistic
    1056             cost = (abs((x2-x1))^obj.exp)*obj.weight;
    1057         end % minDiff
    1058
    1059
    1060         %% maxDiff() FUNCTION
    1061         function cost = maxDiff(obj,x1,x2)
    1062             % cost function minimized by maximizing differences on the stat.  Many uses may want to use orderedMaxDiff, instead.
    1063             cost = (-(abs((x2-x1))^obj.exp))*obj.weight;
    1064         end
    1065
    1066         %% orderedMaxDiff() FUNCTION
    1067         function cost = orderedMaxDiff(obj,x1,x2)
    1068             % cost function minimized by maximizing a difference between the two groups that adheres to s1.stat < s2.stat
    1069             if x1 < x2; cost = (-(abs((x2-x1))^obj.exp))*obj.weight;
    1070             else cost = ((abs((x2-x1))^obj.exp))*obj.weight; end;
    1071         end
    1072
    1073         %% cost = acceptSwap(obj)
    1074         function cost = acceptSwap(obj)
    1075             %generic acceptSwap function
    1076             obj.acceptsw();
    1077             cost = acceptSwap@genericConstraint(obj);
    1078         end
    1079
    1080         %% cost = rejectSwap(obj)
    1081         function cost = rejectSwap(obj)
    1082             %generic reject function
    1083             obj.rejectsw();
    1084             cost = rejectSwap@genericConstraint(obj);
    1085         end
    1086
    1087         %% acceptSwapMeans() METHOD
    1088         function acceptSwapMeans(obj)
    1089             % updates variables when the proposed swap is accepted for
    1090             % means
    1091
    1092             if(isnan(obj.swstat1) && isnan(obj.swstat2))
    1093                 %do nothing, this method does not need to update
    1094             elseif(isnan(obj.swstat1) == false && isnan(obj.swstat2) == false)
    1095               obj.stat1=obj.swstat1;
    1096               obj.stat2=obj.swstat2;
    1097
    1098               obj.swstat1 = NaN;
    1099               obj.swstat2 = NaN;
    1100             else
    1101                 error('This should not have happened.  Maybe NaN in data?');
    1102             end
    1103         end
    1104
    1105         %% rejectSwapMeans() METHOD
    1106         function rejectSwapMeans(obj)
    1107             % updates variables when swap is rejected
    1108               obj.swstat1 = NaN;
    1109               obj.swstat2 = NaN;
    1110         end
    1111
    1112          %% acceptSwapStdev() METHOD
    1113         function acceptSwapStdev(obj)
    1114             % updates variables when the proposed swap is accepted for
    1115             % standard deviations
    1116
    1117             if(isnan(obj.swstat1) && isnan(obj.swstat2))
    1118                 %do nothing, this method does not need to update
    1119             elseif(isnan(obj.swstat1) == false && isnan(obj.swstat2) == false)
    1120
    1121 %               disp('clearing swap memory');
    1122               obj.stat1=obj.swstat1;
    1123               obj.stat2=obj.swstat2;
    1124
    1125
    1126               obj.sumx1sq = obj.swpsumx1sq;
    1127               obj.meanx1 = obj.swpmeanx1;
    1128
    1129               obj.sumx2sq = obj.swpsumx2sq;
    1130               obj.meanx2 = obj.swpmeanx2;
    1131
    1132               obj.swpsumx1sq = NaN;
    1133               obj.swpmeanx1 = NaN;
    1134
    1135               obj.swpsumx2sq = NaN;
    1136               obj.swpmeanx2 = NaN;
    1137
    1138               obj.swstat1 = NaN;
    1139               obj.swstat2 = NaN;
    1140             else
    1141                 error('This should not have happened');
    1142             end
    1143         end
    1144
    1145         %% rejectSwapStdev() METHOD
    1146         function rejectSwapStdev(obj)
    1147             % updates variables when the proposed swap is rejected
    1148
    1149               obj.swpsumx1sq = NaN;
    1150               obj.swpmeanx1 = NaN;
    1151
    1152               obj.swpsumx2sq = NaN;
    1153               obj.swpmeanx2 = NaN;
    1154
    1155               obj.swstat1 = NaN;
    1156               obj.swstat2 = NaN;
    1157         end
    1158     end
    1159
    1160
    1161
    1162
    1163     methods (Static)
    1164         %% p = softDistanceConstraintInputParser2Sample() STATIC METHOD
    1165         function p = softDistanceConstraintInputParser2Sample()
    1166             % generates an input parser with parameter / value pairs and validators for the constructor args
    1167             %
    1168             % See constructor help/doc for more details
    1169
    1170             p = inputParser;
    1171
    1172             %NOTE: though these technically are 'optional' according to
    1173             %MATLAB's definition of what an input parser does, the fact
    1174             %that most of their default values will fail validation in all cases
    1175             %makes them de facto required parameters.
    1176
    1177             p.addParamValue('sosObj','null',@(sosObj)strcmp(class(sosObj),'sos'));
    1178             p.addParamValue('constraintType', 'null', ...
    1179                 @(constraintType)any(strcmp({'soft'},constraintType)));
    1180             p.addParamValue('fnc','null', ...
    1181                  @(fnc)any(strcmp({'min' 'max','orderedMax'},fnc)));
    1182             p.addParamValue('stat','null', ...
    1183                  @(stat)any(strcmp({'mean' 'stdev'},stat)));
    1184             p.addParamValue('sample1','null',@(sample1)strcmp(class(sample1),'sample'));
    1185             p.addParamValue('sample2','null',@(sample2)strcmp(class(sample2),'sample'));
    1186             p.addParamValue('s1ColName','',@(s1ColName)ischar(s1ColName));
    1187             p.addParamValue('s2ColName','',@(s2ColName)ischar(s2ColName));
    1188             p.addParamValue('paired','null',@(paired)islogical(paired));
    1189             p.addParamValue('exponent',2,@(exponent)isnumeric(exponent));
    1190             p.addParamValue('weight',1,@(weight)isnumeric(weight));
    1191             p.addParamValue('name','noname',@(name)ischar(name));
    1192
    1193         end % softDistanceConstraintInputParser()
    1194
    1195
    1196
    1197         function p = softDistanceConstraintInputParser1Sample()
    1198             % generates an input parser with parameter / value pairs and validators for the constructor args
    1199             %
    1200             % See constructor help/doc for more details
    1201
    1202             p = inputParser;
    1203
    1204             %NOTE: though these technically are 'optional' according to
    1205             %MATLAB's definition of what an input parser does, the fact
    1206             %that most of their default values will fail validation in all cases
    1207             %makes them de facto required parameters.
    1208
    1209             p.addParamValue('sosObj','null',@(sosObj)strcmp(class(sosObj),'sos'));
    1210             p.addParamValue('constraintType', 'null', ...
    1211                 @(constraintType)any(strcmp({'soft'},constraintType)));
    1212             p.addParamValue('fnc','null', ...
    1213                  @(fnc)any(strcmp({'match1SampleVal'},fnc)));
    1214             p.addParamValue('stat','null', ...
    1215                  @(stat)any(strcmp({'mean' 'stdev'},stat)));
    1216             p.addParamValue('sample1','null',@(sample1)strcmp(class(sample1),'sample'));
    1217             p.addParamValue('s1ColName','',@(s1ColName)ischar(s1ColName));
    1218             p.addParamValue('exponent',2,@(exponent)isnumeric(exponent));
    1219             p.addParamValue('weight',1,@(weight)isnumeric(weight));
    1220             p.addParamValue('targVal','null',@(targVal)isnumeric(targVal));
    1221             p.addParamValue('name','noname',@(name)ischar(name));
    1222
    1223         end % softDistanceConstraintInputParser()
    1224
    1225     end
    1226
    1227
    1228     methods (Static, Access = private)
    1229
    1230         %% p = parse2SampleContructorArgs(varargin) STATIC PRIVATE FUNCTION
    1231         function p = parse2SampleConstructorArgs(varargin)
    1232             % parses the constructor args
    1233             %
    1234             % See constructor help/doc for more info
    1235
    1236             varargin = varargin{1};
    1237             p = softDistanceConstraint.softDistanceConstraintInputParser2Sample();
    1238             p.parse(varargin{:});
    1239         end
    1240
    1241         %% p = parse1SampleContructorArgs(varargin) STATIC PRIVATE FUNCTION
    1242         function p = parse1SampleConstructorArgs(varargin)
    1243             % parses the constructor args
    1244             %
    1245             % See constructor help/doc for more info
    1246
    1247             varargin = varargin{1};
    1248             p = softDistanceConstraint.softDistanceConstraintInputParser1Sample();
    1249             p.parse(varargin{:});
    1250         end
    1251     end
    1252
    1253 end % end class
    1254