Code covered by the BSD License  

Highlights from
Bidirectional Branch and Bound for Average Loss Minimization

image thumbnail
from Bidirectional Branch and Bound for Average Loss Minimization by Yi Cao
Two algorithms for selection of controlled variables using the average loss as the criterion.

[B,sset,ops,ctime,flag]=pb3av(G1,Gd1,Wd,Wn,Juu,Jud,n,tlimit,nc)
function [B,sset,ops,ctime,flag]=pb3av(G1,Gd1,Wd,Wn,Juu,Jud,n,tlimit,nc)
% PB3AV Partial Bidirectional Branch and Bound (PB3) for Average Loss Criterion 
%
%   [B,sset,ops,ctime,flag] = pb3av(G1,Gd1,Wd,Wn,Juu,Jud,n,tlimit,nc) is an
%   implementation of PB3 algorithm to select measurements, whose 
%   combinations can be used as controlled variables. The measurements are 
%   selected to provide minimum average loss in terms of self-optimizing
%   control based on the following static optimization problem:
%
%       min J = f(y,u,d)
%       s.t.
%           y = G1 * u + G_d1 * Wd * d + Wn * e
%
%  where y is measurement vector (ny), u is input vector (nu), d is disturance
%  vector (nd) and e is the control error vector (ny).
%
%  The average loss is defined based on the assumption that 
%
%       ||d||_2 = 1 and ||e||_1 = 1
%
% Inputs:
%   G1 = process model
%   Gd1 = disturbance model
%   Wd  = diagonal matrix containing magnitudes of disturbances
%   Wn = diagonal matrix containing magnitudes of implementation errors
%   Juu = \partial^2 J/\partial u^2
%   Jud = \partial^2 J/\partial u\partial d
%   n is the number of measurements to be selected, nu <= n <= ny.
%   tlimit defines the time limit to run the code. Default is Inf.
%   nc determines the number of best subsets to be selected. Default is 1.
%
% Outputs:
%   B     - nc x 1 vector of the average loss of selected subsets.
%   sset  - nc x nu indices of selected subsets
%   ops   - number of nodes evaluated.
%   ctime - computation time used
%   flag  - 0 if successful, 1 otherwise (for tlimit < Inf).
%
% References:
%  [1]    I. J. Halvorsen, S. Skogestad, J. C. Morud, and V. Alstad.
%  Optimal selection of controlled variables. Ind. Eng. Chem. Res.,
%  42(14):3273-3284, 2003.  
%  [2]   V. Kariwala, Y. Cao, and S. Janardhanan. Local self-optimizing 
%  control with average loss minimization. Ind. Eng. Chem. Res., 
%  47(4):1150-1158, 2008.
%  [3]    V. Kariwala and Y. Cao, Bidirectional Branch and Bound for
%  Controlled Variable Selection: Part III. Local Average Loss Minimization,
%  IEEE Transactions on Industrial Informatics, Vol. 6, No. 1, pp. 5461,
%  2010.
%
%  See also b3msv, b3wc, pb3av, randcase

%  By Yi Cao at Cranfield University, 17th November 2009; 23rd February 2010.
%

% Example.
%{
ny=40;
nu=15;
nd=5;
[G,Gd,Wd,Wn,Juu,Jud] = randcase(ny,nu,nd);
[B,sset,ops,ctime] = pb3av(G,Gd,Wd,Wn,Juu,Jud,25);
% It takes about 4 seconds
%}

% Default inputs and outputs
    flag=false;
    if nargin<9
        nc=1;
    end
    if nargin<8
        tlimit=Inf;
    end
    ctime0=cputime;
    [r,m] = size(G1);
    if nargin<7
        n=m;
    end
    if n<m
        error('n must larger than number of inputs.')
    end
    % prepare matrices
    Y = [(G1*(Juu\Jud)-Gd1)*Wd Wn];
    G = G1/sqrtm(Juu);
    Y2=Y*Y';
    G2=G*G';
    diagY2=diag(Y2);
    diagG2=diag(G2);
    q2=diagY2./diagG2;
    p2=q2;
    ops=zeros(1,4);
    B=Inf(nc,1);
    sset=zeros(nc,n);
    ib=1;
    bound=Inf;
    % counters: 1) terminal; 2) nodes; 3) sub-nodes; 4) calls    
    ops=[0 0 0 0];
    fx=false(1,r);
    rem=true(1,r);
    nf=0;
    n2=n;
    m2=r;
    % initialize flags
    f=false;
    bf=false;
    downf=false;
    upf=false;
    upff=m>=n2;
    E=eye(r);
    Gd=G;
    D=chol(Y2)'\G;
    D=chol(D'*D);
    b0=trace(D\(D'\eye(m)));
    Ed=zeros(r);
    Zd=G';
    % the recursive solver
    bbavsub(fx,rem,b0);
    % convert bound to loss
    [B,idx]=sort(B/(6*(size(Wd,2)+n)));
    sset=sort(sset(idx,:),2);
    ctime=cputime-ctime0;
    
    function bf0=bbavsub(fx0,rem0,b0)
        % recursive solver
        bf0=m;
        if cputime-ctime0>tlimit
            flag=true;
            return;
        end
        ops(4)=ops(4)+1;
        fx=fx0;
        rem=rem0;
        nf=sum(fx);
        m2=sum(rem);
        n2=n-nf;
        while ~f && m2>n2 && n2>=0  % Loop for second branchs
            while ~f && m2>n2 && n2>0 && (~downf || ~upf || ~bf)
                % Loop for bidirection pruning
                if (~upf || ~bf) && n2 <= m %&& bound < Inf
                    % upwards pruning
                    b0=upprune(b0);
                else
                    upf=true;
                end
                if ~f &&  m2>n2 && m2>0 && (~downf || ~bf)
                    % downwards pruning
                    b0=downprune(b0);
                end
                bf=true;
            end %pruning loop
            if f || m2<n2 || n2<0
                % pruned cases
                return
            elseif m2==n2 || ~n2
                % terminal cases
                break
            end
            if m2-n2==1     % one more element to be removed
                p2(~rem)=Inf;
                if min(p2)<bound
                    idx=find(rem);
                    s=fx|rem;
                    S=s(ones(m2,1),:);
                    S((1:m2)+(idx-1)*m2)=false;
                    [B1,bidx]=sort([B;p2(rem)]);
                    B=B1(1:nc);
                    bound=B(nc);
                    ib=nc;
                    [J,I]=find(S');
                    iS=[sset;reshape(J,[],max(I))'];
                    sset=iS(bidx(1:nc),:);
                    bf=false;
                end
                return
            end
            if n2==1,   % one more element to be fixed
                if upff
                    q2(~rem)=0;
                    [b,idk]=max(q2);
                else
                    p2(~rem)=Inf;
                    [b,idk]=min(p2);
                end
                s=fx;
                s(idk)=true;
                bf0=update(s)+1;
                if bf0<0
                    return;
                end
                rem(idk)=false;
                m2=m2-1;
                downf=false;
                upf=true;
                b0=p2(idk);
                continue
            end
            % save data for bidirectional branching
            fx1=fx;
            rem1=rem;
            b1=bound;
            p0=p2;
            q0=q2;
            n20=n2;
            bb=b0;
            upff0=upff;
            Z0=Zd;
            E0=Ed;
            if n2<0.8*m2    %upward branching
                p2(~rem)=0;
                [b,id]=max(p2);
                fx(id)=true;
                rem1(id)=false;
                upf=false;
                upff=false;
                bf0=bbavsub(fx,rem1,b0)+n20;
                if p0(id)>=bound
                    return
                end
                if bf0<0
                    return
                end
                downf=false;
                upf=true;
                upff=upff0;
                bb=p0(id);
            else            % downward branching
                p2(~rem)=Inf;
                [b,id]=min(p2);
                fx1(id)=true;
                downf=false;
                rem1(id)=false;
                bf0=bbavsub(fx,rem1,b)+n20;
                if upff0 && q2(id)>=bound
                    return
                end
                downf=true;
                upf=false;
                upff=false;
                if bf0<0
                    return
                end
            end
            fx=fx1;
            rem=rem1;
            % check pruning conditions
            if b1>bound
                bf=false;
                p0(~rem)=0;
                L2=p0'>=bound;
                if any(L2)
                    rem=rem&~L2;
                    fx=fx|L2;
                end
                if upff0
                    q0(~rem)=0;
                    L2=q0'>=bound;
                    if any(L2)
                        rem=rem&~L2;
                        if sum(L2)==1
                            bb=p0(L2);
                        else
                            bb=-1;
                        end
                    end
                end
            end
            % Recover data saved before the first branch
            f=false;
            p2=p0;
            q2=q0;
            nf=sum(fx);
            n2=n-nf;
            m2=sum(rem);
            b0=bb;
            Ed=E0;
            Zd=Z0;
        end
        if ~f       % terminal cases
            if m2==n2
                bf0=update(fx|rem);
            elseif ~n2
                bf0=update(fx);
            end
        end
    end

    function b0=upprune(b0)
        % Partially upwards pruning
        if ~upf
            upf=true;
            R1=chol(Y2(fx,fx));     % nf x nf
            X1=R1'\G(fx,:);         % nf x m
            D=X1'*X1;               % m x m
            tD=sort(eig(D),'descend');
            ops(2)=ops(2)+1;
            k1=(m-n2+1);
            x=sum(tD(1:k1));
            k2=k1*k1;
            if k2<=x*bound
                upff=false;
                return
            end
            bf0=sum(cumsum(1./tD)<bound);
            if bf0<m-n2           % current node is not optimal
                f=true;
                return
            end
            if bf0>m-n2+1          % no any sub-node pruning
                upff=false;
                return
            end
            ops(3)=ops(3)+m2;
            Z=R1'\Y2(fx,rem);
            X2=X1'*Z-G(rem,:)';
            eta=diag(Y2(rem,rem))'-sum(Z.*Z,1);
            q2(rem)=k2./(x+sum(X2.*X2,1)./eta);
            upff=true;
        end
        if upff
            L=q2>=bound;
            L(~rem)=false;
            if any(L)
                downf=false;
                rem(L)=false;
                m2=sum(rem);
                if sum(L)==1
                    b0=p2(L);
                else
                    b0=-1;
                end
            end
        end
    end

    function b0=downprune(b0)
        % downwards pruning
        if ~downf
            s0=fx|rem;
            id=p2==b0;
            if sum(id)==1
                u=Ed(id,rem);
                x=u/Ed(id,id);
                NZ=Zd(:,rem)-Zd(:,id)*x;
                X=Ed(rem,rem)-u'*x;                
            else
                R1=chol(Y2(s0,s0));
                D=R1'\G(s0,:);
                [R,f]=chol(D'*D);
                if f    % singular, hence prune
                    return
                end
                R2=R1'\E(s0,rem);
                Gd=R1\D;
                Z=Gd(rem(s0),:)';
                NZ=R\(R'\Z);
                X=R2'*R2-Z'*NZ;
                if b0<0
                    R1=R'\eye(m);
                    b0=sum(sum(R1.*R1));
                end
            end
            Zd(:,rem)=NZ;
            Ed(rem,rem)=X;
            p2(rem)=b0+sum(NZ.*NZ,1)./diag(X)';
            ops(2)=ops(2)+1;
            ops(3)=ops(3)+m2;
            downf=true;
        end
        L=p2>=bound;
        L(~rem)=false;
        if any(L)
            upf=false;
            upff=false;
            fx(L)=true;
            rem(L)=false;
            nf=sum(fx);
            m2=sum(rem);
            n2=n-nf;
        end
    end

    function bf0=update(s)
        X=chol(Y2(s,s))'\G(s,:);
        tD=cumsum(1./sort(eig(X'*X),'descend'));
        bf0=sum(tD<bound)-m;
        bf=bf0<0;
        ops(1)=ops(1)+1;
        if ~bf,
            B(ib)=tD(end);         %avoid sorting
            sset(ib,:)=find(s);
            [bound,ib]=max(B);
        end
    end
end

Contact us at files@mathworks.com