Home > src > softMatchCorrelConstraint.m

# softMatchCorrelConstraint

## PURPOSE

- soft correlation matching constraint object

## SYNOPSIS

This is a script file.

## DESCRIPTION

     - soft correlation matching constraint object

     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
- softMatchCorrelConstraint - soft correlation matching constraint object
- verbosePrint - master print function that regulates cmd line output based on verbosity flags

This function is called by:

- genericConstraint - parent class for constraints
- softMatchCorrelConstraint - soft correlation matching constraint object

## SUBFUNCTIONS

- function obj = softMatchCorrelConstraint(varargin)
- function cost = initCost(obj)
- function swCost = swapCost(obj,targSample,targSampleIndex, feederdf,feederdfIndex)
- function obj = initCorrel(obj)
- function swCorrel(obj,targSample,targSampleIndex, feederdf,feederdfIndex)
- function cost = matchCorrel(obj,x1,x2)
- function cost = acceptSwap(obj)
- function cost = rejectSwap(obj)
- function acceptSwapCorrel(obj)
- function rejectSwapCorrel(obj)
- function obj = constructSoftMatchCorrelConstraint(obj,varargin)
- function p = softMatchCorrelConstraintInputParserMatchCorrel()
- function p = parseMatchCorrelConstructorArgs(varargin)

## SOURCE CODE

    0001 % - soft correlation matching constraint object
    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 classdef softMatchCorrelConstraint < softConstraint
    0024     %% creates and supports soft matchCorrel constraints
    0025     %
    0026     % This class creates softMatchCorrelConstraint objects that measure the
    0027     % cost (in terms of matching a correlation across two variables)
    0028     % associated with the current items in a set, and subsequent to
    0029     % swapping a particular item with another items.
    0030     %
    0031     % Additional functionality and interface requirements are inherited
    0032     % from softConstraint.
    0033     %
    0034     % a beautiful property:
    0035     % correlation == covariance / stdev(x)stdev(y)
    0036     % BUT because both numerator and denominators standardize for number of
    0037     % observations, this cancels each other out, so also:
    0038     %   cor = SPxy / sqrt(SSx)*sqrt(SSy), where SP = sum of products
    0039     % This can be reduced to a set of very computationally efficient
    0040     % equations:
    0041     % SSx = Sum(x^2) - (Sum(x))^2/N  --- i.e., subtract correction factor
    0042     % AND: SPxy = Sum(xy) - Sum(x)*Sum(y)/N
    0043     % Gotta love algebra.  Pythagoras would be all over this...
    0044     %
    0045     % PROPERTIES
    0046     %     stat % the statistic to calculate (correlation)
    0047     %     comparison % handle to specific comparison calculator
    0048     %     initStats % handle to global stat initialization method
    0049     %     swStats % handle to local stat update method
    0050     %     acceptsw % handle to local accept swap method
    0051     %     rejectsw % handle to local reject swap method
    0052     %     s1 % sample1
    0053     %     s2 % sample2
    0054     %     s1Col %s1 column index
    0055     %     s2Col %s2 column index
    0056     %     s1ColName % name of column in s1
    0057     %     s2ColName % name of column in s2
    0058     %     targVal % target value to match for 1 sample-to-value matching
    0059     %     sumx1sq % sum of squares in sample 1
    0060     %     sumx1 % sum of sample 1
    0061     %     n1 % number of observations in sample 1
    0062     %     sumx2sq % same for sample 2
    0063     %     sumx2 % same for sample 2
    0064     %     n2 % same for sample 2
    0065     %     sumx1x2 % sum of product of x1 and x2, across all items
    0066     %     swpsumx1sq % same as above for swap set
    0067     %     swpsumx2sq % same as above for swap set
    0068     %     swpsumx1 % same as above for swap set
    0069     %     swpsumx2 % same as above for swap set
    0070     %     swpsumx1x2 % same as above for swap set
    0071     %     cor % the correlation between the two sets
    0072     %     swpcor % the correlation if the swap was applied.
    0073     %
    0074     % METHODS
    0075     %   function obj = softMatchCorrelConstraint(varargin) % constructor
    0076     %   cost = initCost() - Calculates, saves, and returns the cost value for the current items in the sample.
    0077     %   swCost = swapCost(targSample,targSampleIndex, feederdf,feederdfIndex) % Calculates the new cost if items from targSample and feederdf were swapped.
    0078     %   initCorrel() FUNCTION calculates the correlation and initializes the parameters needed for local stat updates
    0079     %   swCorrel(targSample,targSampleIndex, feederdf,feederdfIndex) % calculates the correlation if the swap were applied
    0080     %   matchCorrel(x1,x2) % cost function minimized by reduced differences on the statistic
    0081     %   acceptSwap() % generic acceptSwap function
    0082     %   cost = rejectSwap() % generic reject function
    0083     %   acceptSwapCorrel() % updates variables when the proposed swap is accepted for means
    0084     %   rejectSwapCorrel() % resets swap variables
    0085     %   constructSoftMatchCorrelConstraint(varargin)  % creates a constraint object
    0086     %
    0087     % METHODS (Static)
    0088     %   softMatchCorrelConstraintInputParserMatchCorrel() % generates an input parser with parameter / value pairs and validators for the constructor args
    0089     %
    0090     % METHODS (Static, Access = private)
    0091     %   p = parseMatchCorrelConstructorArgs(varargin) % parses the constructor args
    0092
    0093     %% PROPERTIES
    0094     properties
    0095         stat % the statistic to calculate (correlation)
    0096         comparison % handle to specific comparison calculator
    0097         initStats % handle to global stat initialization method
    0098         swStats % handle to local stat update method
    0099         acceptsw % handle to local accept swap method
    0100         rejectsw % handle to local reject swap method
    0101         s1 % sample1
    0102         s2 % sample2
    0103         s1Col %s1 column index
    0104         s2Col %s2 column index
    0105         s1ColName % name of column in s1
    0106         s2ColName % name of column in s2
    0107         targVal % target value to match for 1 sample-to-value matching
    0108         sumx1sq % sum of squares in sample 1
    0109         sumx1 % sum of sample 1
    0110         n1 % number of observations in sample 1
    0111         sumx2sq % same for sample 2
    0112         sumx2 % same for sample 2
    0113         n2 % same for sample 2
    0114         sumx1x2 % sum of product of x1 and x2, across all items
    0115         swpsumx1sq % same as above for swap set
    0116         swpsumx2sq % same as above for swap set
    0117         swpsumx1 % same as above for swap set
    0118         swpsumx2 % same as above for swap set
    0119         swpsumx1x2 % same as above for swap set
    0120         cor % the correlation between the two sets
    0121         swpcor % the correlation if the swap was applied.
    0122
    0123     end
    0124
    0125
    0126     %% METHODS
    0127     methods
    0128
    0129         %% softMatchCorrelConstraint CONSTRUCTOR
    0130         function obj = softMatchCorrelConstraint(varargin)
    0131             % Creates a soft distance constraint.  See the parse
    0132             % constructor args functions for parameters.
    0133
    0134             p = inputParser;
    0135             p.addParamValue('fnc','null', ...
    0136                 @(fnc)any(strcmp({'matchCorrel'},fnc)));
    0137             p.KeepUnmatched = true;
    0138             p.parse(varargin{:});
    0139
    0140             if any(strcmp(p.Results.fnc,'matchCorrel'))
    0141                 % can create the matchCorrel constraint
    0142                 obj = constructSoftMatchCorrelConstraint(obj,varargin);
    0143             else
    0144                 error(['Could not create a soft constraint with <fnc>: ', ...
    0145                         p.Results.fnc]);
    0146             end
    0147
    0148             obj.cost = NaN;
    0149             obj.swCost = NaN;
    0150
    0151
    0152             verbosePrint('Soft matchCorrel Constraint has been created', ...
    0153                     'softMatchCorrelConstraint_Constructor_endObjCreation');
    0154
    0155         end %constructor
    0156
    0157
    0158         %% cost = initCost() METHOD
    0159         function cost = initCost(obj)
    0160             % Calculates, saves, and returns the cost value for the current items in the sample.
    0161             %
    0162             %CALL:
    0163             %   <softDistanceConstraint>.initCost();
    0164             %
    0165             %SYNOPSIS:
    0166             % Calculates the cost for the current items in the sample based
    0167             % on the specified constraint.  This calculation is done on the
    0168             % global dataset, rather than using the differential local
    0169             % computation used when calculating swap costs.
    0170             %
    0171
    0172             %init the stats, then compare them.  Return the calculated cost
    0173             obj = obj.initStats();
    0174             cost = obj.comparison(obj.targVal,obj.cor); %where we match the correlation...
    0175
    0176             obj.swpcor = NaN;
    0177             obj.swpsumx1sq = NaN;
    0178             obj.swpsumx2sq = NaN;
    0179             obj.swpsumx1 = NaN;
    0180             obj.swpsumx2 = NaN;
    0181             obj.swpsumx1x2 = NaN;
    0182
    0183             obj.cost = cost;
    0184             obj.swCost = NaN;
    0185         end
    0186
    0187         %%  swCost(targSample,targSampleIndex, feederdf,feederdfIndex) METHOD
    0188         function swCost = swapCost(obj,targSample,targSampleIndex, feederdf,feederdfIndex)
    0189             % Calculates the new cost if items from targSample and feederdf were swapped.
    0190             %
    0191             %By definition, if this method is called it means that at least
    0192             % one of the two swap objects is implicated in this function
    0193             %
    0194             %PARAMETERS:
    0195             %   targSample - the target sample (i.e., the object that will call it's swapSample() method if a swap later occurs).
    0196             %   targSampleIndex - row index of item to swap
    0197             %   feederdf - dataframe (sample/pop) containin the other item to swap
    0198             %   feederdfIndex - row index of item to swap.
    0199
    0200            if (obj.s1 ~= targSample && obj.s2 ~= targSample ...
    0201                    && obj.s1 ~= feederdf && obj.s2  ~= feederdf)
    0202                error('swCost called, but no sample part of this cost function');
    0203            end
    0204
    0205
    0206            % update the stats, then calculate cost of new stats
    0207            obj.swStats(targSample,targSampleIndex, feederdf,feederdfIndex);
    0208
    0209
    0210             swCost = obj.comparison(obj.targVal,obj.swpcor);
    0211             obj.swCost = swCost;
    0212
    0213         end
    0214
    0215         %% obj = initCorrel() METHOD
    0216         function obj = initCorrel(obj)
    0217             %  calculates the correlation and initializes the parameters
    0218             %  needed for local stat updates
    0219
    0220             obj.sumx1sq = sum((obj.s1.zdata{obj.s1Col}).^2);
    0221             obj.sumx1 = sum((obj.s1.zdata{obj.s1Col}));
    0222             obj.n1 = length(obj.s1.zdata{obj.s1Col});
    0223
    0224             obj.sumx2sq = sum((obj.s2.zdata{obj.s2Col}).^2);
    0225             obj.sumx2 = sum((obj.s2.zdata{obj.s2Col}));
    0226             obj.n2 = length(obj.s2.zdata{obj.s2Col});
    0227
    0228             obj.sumx1x2 = (obj.s1.zdata{obj.s1Col})' * (obj.s2.zdata{obj.s2Col});
    0229
    0230             ssx1 = obj.sumx1sq - ((obj.sumx1)^2)/obj.n1;
    0231             ssx2 = obj.sumx2sq - ((obj.sumx2)^2)/obj.n2;
    0232
    0233             spx1x2 = obj.sumx1x2 - obj.sumx1*obj.sumx2/obj.n1;
    0234
    0235
    0236             if(ssx1 == 0 && ssx2 == 0) % no variance in either condition, define correlation as being perfect in this case
    0237                 obj.cor = 1;
    0238             elseif(ssx1 == 0 || ssx2 == 0)
    0239                 obj.cor = 0;
    0240             else % compute correlation normally
    0241                 obj.cor = spx1x2/(sqrt(ssx1)*sqrt(ssx2));
    0242             end
    0243
    0244             obj.swpcor = NaN;
    0245             obj.swpsumx1sq = NaN;
    0246             obj.swpsumx2sq = NaN;
    0247             obj.swpsumx1 = NaN;
    0248             obj.swpsumx2 = NaN;
    0249             obj.swpsumx1x2 = NaN;
    0250         end
    0251
    0252
    0253         %% swCorrel(targSample,targSampleIndex,feederdf,feederdfIndex) METHOD
    0254         function swCorrel(obj,targSample,targSampleIndex, feederdf,feederdfIndex)
    0255             % calculates the correlation if the swap were applied
    0256             obj.swpsumx1sq = obj.sumx1sq;
    0257             obj.swpsumx2sq = obj.sumx2sq;
    0258             obj.swpsumx1 = obj.sumx1;
    0259             obj.swpsumx2 = obj.sumx2;
    0260             obj.swpsumx1x2 = obj.sumx1x2;
    0261
    0262             %NOTE: if both target and feeder are identical, swapping items
    0263             %within those two samples will not change the correlation.
    0264             %This could be leveraged to simplify the conditionalization.
    0265
    0266              if targSample == obj.s1
    0267                 %must update 2 params %first, take out the old observation, then add in thew new
    0268                 obj.swpsumx1sq = obj.swpsumx1sq - (targSample.zdata{obj.s1Col}(targSampleIndex))^2;
    0269                 obj.swpsumx1 = obj.swpsumx1 - (targSample.zdata{obj.s1Col}(targSampleIndex));
    0270                 obj.swpsumx1sq = obj.swpsumx1sq + (feederdf.zdata{obj.s1Col}(feederdfIndex))^2;
    0271                 obj.swpsumx1 = obj.swpsumx1 + (feederdf.zdata{obj.s1Col}(feederdfIndex));
    0272              end
    0273
    0274              if targSample == obj.s2
    0275                 obj.swpsumx2sq = obj.swpsumx2sq - (targSample.zdata{obj.s2Col}(targSampleIndex))^2;
    0276                 obj.swpsumx2 = obj.swpsumx2 - (targSample.zdata{obj.s2Col}(targSampleIndex));
    0277                 obj.swpsumx2sq = obj.swpsumx2sq + (feederdf.zdata{obj.s2Col}(feederdfIndex))^2;
    0278                 obj.swpsumx2 = obj.swpsumx2 + (feederdf.zdata{obj.s2Col}(feederdfIndex));
    0279              end
    0280
    0281
    0282             if feederdf == obj.s1
    0283                 obj.swpsumx1sq = obj.swpsumx1sq -  (feederdf.zdata{obj.s1Col}(feederdfIndex))^2;
    0284                 obj.swpsumx1 = obj.swpsumx1 - (feederdf.zdata{obj.s1Col}(feederdfIndex));
    0285                 obj.swpsumx1sq = obj.swpsumx1sq + (targSample.zdata{obj.s1Col}(targSampleIndex))^2;
    0286                 obj.swpsumx1 = obj.swpsumx1 + (targSample.zdata{obj.s1Col}(targSampleIndex));
    0287             end
    0288
    0289             if feederdf == obj.s2
    0290                 obj.swpsumx2sq = obj.swpsumx2sq -  (feederdf.zdata{obj.s2Col}(feederdfIndex))^2;
    0291                 obj.swpsumx2 = obj.swpsumx2 - (feederdf.zdata{obj.s2Col}(feederdfIndex));
    0292                 obj.swpsumx2sq = obj.swpsumx2sq + (targSample.zdata{obj.s2Col}(targSampleIndex))^2;
    0293                 obj.swpsumx2 = obj.swpsumx2 + (targSample.zdata{obj.s2Col}(targSampleIndex));
    0294             end
    0295
    0296             % Special overrides
    0297
    0298             if targSample == obj.s1 && targSample ~= obj.s2
    0299                 obj.swpsumx1x2 = obj.swpsumx1x2 - (targSample.zdata{obj.s1Col}(targSampleIndex)) * obj.s2.zdata{obj.s2Col}(targSampleIndex);
    0300                 obj.swpsumx1x2 = obj.swpsumx1x2 + (feederdf.zdata{obj.s1Col}(feederdfIndex))     * obj.s2.zdata{obj.s2Col}(targSampleIndex);
    0301             elseif targSample == obj.s2 && targSample ~= obj.s1
    0302                 obj.swpsumx1x2 = obj.swpsumx1x2 - (targSample.zdata{obj.s2Col}(targSampleIndex)) * obj.s1.zdata{obj.s1Col}(targSampleIndex);
    0303                 obj.swpsumx1x2 = obj.swpsumx1x2 + (feederdf.zdata{obj.s2Col}(feederdfIndex))     * obj.s1.zdata{obj.s1Col}(targSampleIndex);
    0304             elseif targSample == obj.s1 && targSample == obj.s2
    0305                 obj.swpsumx1x2 = obj.swpsumx1x2 - (obj.s1.zdata{obj.s1Col}(targSampleIndex)) * obj.s2.zdata{obj.s2Col}(targSampleIndex);
    0306                 obj.swpsumx1x2 = obj.swpsumx1x2 + (feederdf.zdata{obj.s1Col}(feederdfIndex))     * feederdf.zdata{obj.s2Col}(feederdfIndex);
    0307             end
    0308
    0309
    0310              if feederdf == obj.s1 && feederdf ~= obj.s2
    0311                 obj.swpsumx1x2 = obj.swpsumx1x2 - (feederdf.zdata{obj.s1Col}(feederdfIndex))     * obj.s2.zdata{obj.s2Col}(feederdfIndex);
    0312                 obj.swpsumx1x2 = obj.swpsumx1x2 + (targSample.zdata{obj.s1Col}(targSampleIndex)) * obj.s2.zdata{obj.s2Col}(feederdfIndex);
    0313              elseif feederdf == obj.s2 && feederdf ~= obj.s1
    0314                 obj.swpsumx1x2 = obj.swpsumx1x2 - (feederdf.zdata{obj.s2Col}(feederdfIndex))     * obj.s1.zdata{obj.s1Col}(feederdfIndex);
    0315                 obj.swpsumx1x2 = obj.swpsumx1x2 + (targSample.zdata{obj.s2Col}(targSampleIndex)) * obj.s1.zdata{obj.s1Col}(feederdfIndex);
    0316                elseif feederdf == obj.s1 && feederdf == obj.s2
    0317                 obj.swpsumx1x2 = obj.swpsumx1x2 - (obj.s1.zdata{obj.s1Col}(feederdfIndex)) * obj.s2.zdata{obj.s2Col}(feederdfIndex);
    0318                 obj.swpsumx1x2 = obj.swpsumx1x2 + (targSample.zdata{obj.s1Col}(targSampleIndex))     * targSample.zdata{obj.s2Col}(targSampleIndex);
    0319              end
    0320
    0321             if((targSample ~= feederdf) && ...
    0322                     (targSample == obj.s1 || targSample == obj.s2) && ...
    0323                     (feederdf == obj.s1 || feederdf == obj.s2) && ...
    0324                     targSampleIndex == feederdfIndex  && ...
    0325                     obj.s1Col == obj.s2Col)
    0326                 obj.swpsumx1x2 = obj.sumx1x2;
    0327             end
    0328
    0329
    0330            if((targSample ~= feederdf) && ...
    0331                     (targSample == obj.s1 || targSample == obj.s2) && ...
    0332                     (feederdf == obj.s1 || feederdf == obj.s2) && ...
    0333                     targSampleIndex == feederdfIndex  && ...
    0334                     obj.s1Col ~= obj.s2Col)
    0335
    0336                     obj.swpsumx1x2 = obj.sumx1x2 - obj.s1.zdata{obj.s1Col}(targSampleIndex) * obj.s2.zdata{obj.s2Col}(feederdfIndex) + ...
    0337                         obj.s2.zdata{obj.s1Col}(feederdfIndex) * obj.s1.zdata{obj.s2Col}(targSampleIndex);
    0338            end
    0339
    0340
    0341             swpssx1 = obj.swpsumx1sq - ((obj.swpsumx1)^2)/obj.n1;
    0342             swpssx2 = obj.swpsumx2sq - ((obj.swpsumx2)^2)/obj.n2;
    0343
    0344             swpspx1x2 = obj.swpsumx1x2 - obj.swpsumx1*obj.swpsumx2/obj.n1;
    0345
    0346
    0347             if(swpssx1 == 0 && swpssx2 == 0) % no variance in either condition, define correlation as being perfect in this case
    0348                 obj.swpcor = 1;
    0349             elseif(swpssx1 == 0 || swpssx2 == 0)
    0350                 obj.swpcor = 0;
    0351             else % compute correlation normally
    0352                 obj.swpcor = swpspx1x2/(sqrt(swpssx1)*sqrt(swpssx2));
    0353             end
    0354
    0355
    0356         end
    0357
    0358
    0359
    0360         %% matchCorrel() METHOD
    0361         function cost = matchCorrel(obj,x1,x2)
    0362             % cost function minimized by reduced differences on the statistic
    0363             cost = ((abs(x2-x1)/2)^obj.exp)*obj.weight;
    0364
    0365         end % matchCorrel
    0366
    0367
    0368         %% cost = acceptSwap() METHOD
    0369         function cost = acceptSwap(obj)
    0370             %generic acceptSwap function
    0371             obj.acceptsw();
    0372             cost = acceptSwap@genericConstraint(obj);
    0373         end
    0374
    0375         %% cost = rejectSwap()  METHOD
    0376         function cost = rejectSwap(obj)
    0377             %generic reject function
    0378             obj.rejectsw();
    0379             cost = rejectSwap@genericConstraint(obj);
    0380         end
    0381
    0382         %% acceptSwapCorrel() METHOD
    0383         function acceptSwapCorrel(obj)
    0384             % updates variables when the proposed swap is accepted for
    0385             % means
    0386
    0387             if(isnan(obj.swpcor))
    0388                 %do nothing, this method does not need to update
    0389             else
    0390             obj.sumx1sq = obj.swpsumx1sq;
    0391             obj.sumx2sq = obj.swpsumx2sq;
    0392             obj.sumx1 = obj.swpsumx1;
    0393             obj.sumx2 = obj.swpsumx2;
    0394             obj.sumx1x2 = obj.swpsumx1x2;
    0395             obj.cor = obj.swpcor;
    0396
    0397
    0398             obj.rejectSwapCorrel();
    0399
    0400             end
    0401         end
    0402
    0403         %% rejectSwapCorrel() METHOD
    0404         function rejectSwapCorrel(obj)
    0405             %resets swap variables
    0406             obj.swpcor = NaN;
    0407             obj.swpsumx1sq = NaN;
    0408             obj.swpsumx2sq = NaN;
    0409             obj.swpsumx1 = NaN;
    0410             obj.swpsumx2 = NaN;
    0411             obj.swpsumx1x2 = NaN;
    0412         end
    0413
    0414         %% obj = constructSoftMatchCorrelConstraint(varargin) METHOD
    0415         function obj = constructSoftMatchCorrelConstraint(obj,varargin)
    0416             % creates a constraint object
    0417             % CONSTRUCTOR - Creates a softDistanceConstraint object
    0418             %
    0419             % CALL:
    0420             % softDistanceConstraint(varargin<defined below>)
    0421             %
    0422             % PARAMETERS:
    0423             % REQUIRED:
    0424             %   'sosObj'/sos object - the SOS object the constraint will be linked to, and which contains the samples the constraint operates on.
    0425             %   'constraintType'/'soft' - the type of contraint - must be 'soft'
    0426             %   'fnc'/'matchCorrel' the distance function to create.
    0427             %   'sample1'/sample - the first sample
    0428             %   'sample2'/sample - the second sample
    0429             %   's1ColName'/string - name of column in 1st sample
    0430             %   's2ColName'/string - name of column in 2nd sample
    0431             %   'targVal'/integer [-1,1] - target correlation to match
    0432
    0433             % OPTIONAL:
    0434             %   'exponent'/numeric - defaults to 2 (quadratic difference)
    0435             %   'weight'/numeric - defaults to 1 (equal weighting of all soft costs)
    0436             %
    0437             % EXAMPLE:
    0438             %     c2 = mySOS.addConstraint('sosObj',mySOS,'name','matchFreqCorrelConstraint','constraintType', ...
    0439             %     'soft','fnc','matchCorrel',...
    0440             %     'sample1',mySample1,'s1ColName','KFfrequency', ...
    0441             %     'sample2',mySample2,'s2ColName','KFfrequency','targVal',1,'exponent',2,'weight',1);
    0442
    0443
    0444             p = softMatchCorrelConstraint.parseMatchCorrelConstructorArgs(varargin{:});
    0445
    0446             if(p.Results.sosObj.containsSample(p.Results.sample1) == false)
    0447                 error('Cannot create soft distance constraint: sos Object does not contain the sample1');
    0448             end
    0449
    0450             if(p.Results.sosObj.containsSample(p.Results.sample2) == false)
    0451                 error('Cannot create soft distance constraint: sos Object does not contain sample2');
    0452             end
    0453
    0454
    0455             col1 = p.Results.sample1.colName2colNum(p.Results.s1ColName);
    0456             if(col1 == -1)
    0457                 error('Specified column name not found in sample1');
    0458             end
    0459
    0460             if(strcmp(p.Results.sample1.format{col1},'%f') == 0)
    0461                 error('Specified column is not of numeric (%f) format, so cannot use as soft constraint');
    0462             end
    0463
    0464             col2 = p.Results.sample2.colName2colNum(p.Results.s2ColName);
    0465             if(col2 == -1)
    0466                 error('Specified column name not found in sample');
    0467             end
    0468
    0469             if(strcmp(p.Results.sample2.format{col2},'%f') == 0)
    0470                 error('Specified column is not of numeric (%f) format, so cannot use as soft constraint; 2nd sample');
    0471             end
    0472
    0473             %if the lengths are not going to be identical, do not run the
    0474             %correlation.  The init and swap methods will ensure
    0475             %that if practically the data are NaN, the optimization will
    0476             %stop.
    0477
    0478             if length(p.Results.sample1.n) ~= length(p.Results.sample2.n)
    0479                 error('Sample sizes must be equal if using paired matching');
    0480             end
    0481
    0482             % Assign the comparison function appropriate for the constraint
    0483
    0484             %ASSIGN HANDLE TO STAT CALCULATION METHOD
    0485
    0486             obj.initStats = @obj.initCorrel;
    0487             obj.swStats = @obj.swCorrel;
    0488             obj.acceptsw = @obj.acceptSwapCorrel;
    0489             obj.rejectsw = @obj.rejectSwapCorrel;
    0490
    0491             % ASSIGN HANDLE TO DIFFERENCE COMPARISON METHOD
    0492             % Group differences:
    0493             if(strcmp(p.Results.fnc,'matchCorrel'))
    0494                 obj.comparison = @obj.matchCorrel;
    0495             else
    0496                 error('function not yet supported');
    0497             end
    0498
    0499             % parent properties
    0500             obj.sosObj = p.Results.sosObj;
    0501             obj.constraintType = p.Results.constraintType;
    0502             obj.fnc = p.Results.fnc;
    0503
    0504
    0505             obj.weight = p.Results.weight;
    0506             obj.exp = p.Results.exponent;
    0507
    0508             if(p.Results.targVal < -1 || p.Results.targVal > 1 || isnan(p.Results.targVal))
    0509                 error('Target correlation value must be a number -1 <= targ <= 1');
    0510             end
    0511
    0512             obj.targVal = p.Results.targVal;
    0513
    0514             obj.s1 = p.Results.sample1;
    0515             obj.s2 = p.Results.sample2;
    0516             obj.s1Col = col1;
    0517             obj.s2Col = col2;
    0518             obj.s1ColName = p.Results.s1ColName;
    0519             obj.s2ColName = p.Results.s2ColName;
    0520
    0521             % add the name and the label
    0522             obj.label = [obj.constraintType,'_',obj.fnc,'_',...
    0523                     obj.targVal,'_',...
    0524                     obj.s1.name,'_',obj.s1ColName,'_',...
    0525                     obj.s2.name,'_',obj.s2ColName,'_',...
    0526                     'p','1','_w',...
    0527                     num2str(obj.weight),'_e',num2str(obj.exp)];
    0528             if any(strcmp(p.UsingDefaults,'name'))
    0529                 obj.name = obj.label;
    0530             else
    0531                  obj.name = p.Results.name;
    0532             end
    0533         end %construct2SampleSoftDistanceConstraint
    0534
    0535
    0536     end
    0537
    0538     methods (Static)
    0539         %% p = softMatchCorrelConstraintInputParserMatchCorrel() STATIC METHOD
    0540         function p = softMatchCorrelConstraintInputParserMatchCorrel()
    0541             % generates an input parser with parameter / value pairs and validators for the constructor args
    0542             %
    0543             % See constructor help/doc for more details
    0544
    0545             p = inputParser;
    0546
    0547             %NOTE: though these technically are 'optional' according to
    0548             %MATLAB's definition of what an input parser does, the fact
    0549             %that most of their default values will fail validation in all cases
    0550             %makes them de facto required parameters.
    0551
    0552             p.addParamValue('sosObj','null',@(sosObj)strcmp(class(sosObj),'sos'));
    0553             p.addParamValue('constraintType', 'null', ...
    0554                 @(constraintType)any(strcmp({'soft'},constraintType)));
    0555             p.addParamValue('fnc','null', ...
    0556                  @(fnc)any(strcmp({'matchCorrel'},fnc)));
    0557             p.addParamValue('sample1','null',@(sample1)strcmp(class(sample1),'sample'));
    0558             p.addParamValue('sample2','null',@(sample2)strcmp(class(sample2),'sample'));
    0559             p.addParamValue('s1ColName','',@(s1ColName)ischar(s1ColName));
    0560             p.addParamValue('s2ColName','',@(s2ColName)ischar(s2ColName));
    0561             p.addParamValue('targVal',NaN,@(targVal)isnumeric(targVal));
    0562             p.addParamValue('exponent',2,@(exponent)isnumeric(exponent));
    0563             p.addParamValue('weight',1,@(weight)isnumeric(weight));
    0564             p.addParamValue('name','noname',@(name)ischar(name));
    0565
    0566         end
    0567
    0568
    0569     end
    0570
    0571     methods (Static, Access = private)
    0572
    0573         %% p = parseMatchCorrelConstructorArgs(varargin) STATIC PRIVATE FUNCTION
    0574         function p = parseMatchCorrelConstructorArgs(varargin)
    0575             % parses the constructor args
    0576             %
    0577             % See constructor help/doc for more info
    0578
    0579             varargin = varargin{1};
    0580             p = softMatchCorrelConstraint.softMatchCorrelConstraintInputParserMatchCorrel();
    0581             p.parse(varargin{:});
    0582         end
    0583
    0584     end
    0585
    0586
    0587 end