function diagnostic = callbmilin(F,h,options)
%
% EXTREMELY naive implementation of a local BMI solver
% using linearizations and a simple trust-region.
%
% To be precise, it not only "solves" problems with BMIs 
% but also PMIs (polynomial matrix inequalities).
%
% Moreover, the objective may be nonlinear.
%
% The default behaviour of the solver can be
% altered by using the field bmilin in sdpsettings.
%
% bmilin.trust          - Trust region norm p in ||x-x0||_p < alpha*||x0||_p+beta [1|2|inf (2)]
% bmilin.alpha          - Trust region parameter [real > 0 (0.5)]
% bmilin.beta           - Trust region parameter [real > 0 (0)]
% bmilin.solver         - Solver for linearized SDP [Any of the solvers above ('')]
% bmilin.maxiterls      - Maximum number of iterations in line search [integer (10)]
% bmilin.maxiter        - Maximum number of iterations [integer (25)]
%

disp('***********************************************')
disp('')
disp('Warning : This code is just for illustration   ')
disp('and should be seen as an example on how you can')
disp('implement your own local BMI solver.')
disp('')
disp('Note also that this solver is slow since it is')
disp('implemented using high-level YALMIP code')
disp('')
disp('***********************************************')

% Recover all used variables
x = recover(depends(F));

% Set up for local solver
verbose = options.verbose;
options.verbose = max(options.verbose-1,0);
options.solver = options.bmilin.solver;

% Initial values hopefully supplied
if options.usex0
    % Initialize to 0 if not initialized
    not_initial = isnan(double(x));
    if any(not_initial)
        setsdpvar(x(find(not_initial)),repmat(0,length(find(not_initial)),1));
    end
else
    % No initials, set to zero
    setsdpvar(x,repmat(0,length(x),1));
    F_linear = F(find(is(F,'linear')));
    % Find some non-zero by solving for the linear constraints
    if length(F_linear) > 0
        sol = solvesdp(F_linear,linearize(h)+x'*x,options);
        if sol.problem~=0
            diagnostic.solvertime = 0;
            diagnostic.info = yalmiperror(0,'BMILIN');
            diagnostic.problem = sol.problem;
            return
        end
    end
end

% Outer linearization loop
goon = 1;
outer_iter = 0;
alpha = 1;
problem = 0;
while goon
    
    % Save old iterates and old objective function
    x0 = double(x);
    h0 = double(h);
    
    % Linearize
    Flin = linearize(F);
    
    % add a trust region
    switch options.bmilin.trust
    case 1
    case 2
        Flin = Flin + set(cone(x-x0,2*alpha*options.bmilin.alpha*norm(x0,2)+options.bmilin.beta));
    case inf
        Flin = Flin + set(x-x0 < options.bmilin.alpha*norm(x0,'inf')+options.bmilin.beta);
        Flin = Flin + set(x-x0 >-options.bmilin.alpha*norm(x0,'inf')+options.bmilin.beta);
    otherwise
    end
    
    % Solve linearized problem
    sol = solvesdp(Flin,linearize(h),options);
    switch sol.problem
    case 0
        % Optimal decision variables for linearized problem
        xnew = double(x);
        
        % Original problem is not guaranteed to be feasible
        % Simple line-search for feasible (and improving) solution
        alpha = 1;
        inner_iter = 0;
        p = checklmi(F);
        while ((min(p)<-1e-5) | (double(h)>h0*1.0001)) & (inner_iter < 15)
            alpha = alpha*0.8;
            setsdpvar(x,x0+alpha*(xnew-x0));
            p = checkset(F);
            inner_iter = inner_iter + 1;
        end
        if inner_iter < 300
            outer_iter = outer_iter + 1;
            if verbose > 0
                disp(sprintf('#%d cost : %6.3f  step : %2.3f',outer_iter,double(h),alpha));
            end        
            goon = (outer_iter < options.bmilin.maxiter);
        else
            problem = 4;
            goon = 0;
        end
        
        
    otherwise
        problem = 4;
        goon = 0;
    end
    
end

diagnostic.solvertime = 0;
diagnostic.info = yalmiperror(problem,'BMILIN');
diagnostic.problem = problem;
