├── FW_video_colocalization ├── solvers │ ├── solver_image.m │ ├── solver_video_mex.mexa64 │ ├── solver_video_mex.mexw64 │ ├── solver_video_mex.mexmaci64 │ ├── solver_images.m │ ├── solver_videos_mex.m │ ├── solver_videos.m │ ├── solver_images_epic.m │ ├── init_images.m │ ├── init_videos.m │ ├── solver_video.m │ ├── solver_video_mex.cpp │ └── mexUtil.h ├── data │ └── aeroplane_data_small.mat ├── COPYING ├── README ├── run_FW.m ├── FW.m ├── PFW.m └── AFW.m ├── triangle_FW_experiment ├── processOptions.m ├── additional_experiment.m ├── pFW.m ├── AFW.m └── run_triangles.m ├── FW_lasso ├── run_FW.m ├── FW_pair.m └── FW.m ├── README.md └── LICENSE /FW_video_colocalization/solvers/solver_image.m: -------------------------------------------------------------------------------- 1 | function y = solver_image(x) 2 | 3 | [~,id] = min(x); 4 | y = zeros(size(x)); 5 | y(id(1)) = 1; 6 | -------------------------------------------------------------------------------- /FW_video_colocalization/data/aeroplane_data_small.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon-Lacoste-Julien/linearFW/HEAD/FW_video_colocalization/data/aeroplane_data_small.mat -------------------------------------------------------------------------------- /FW_video_colocalization/solvers/solver_video_mex.mexa64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon-Lacoste-Julien/linearFW/HEAD/FW_video_colocalization/solvers/solver_video_mex.mexa64 -------------------------------------------------------------------------------- /FW_video_colocalization/solvers/solver_video_mex.mexw64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon-Lacoste-Julien/linearFW/HEAD/FW_video_colocalization/solvers/solver_video_mex.mexw64 -------------------------------------------------------------------------------- /FW_video_colocalization/solvers/solver_video_mex.mexmaci64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon-Lacoste-Julien/linearFW/HEAD/FW_video_colocalization/solvers/solver_video_mex.mexmaci64 -------------------------------------------------------------------------------- /FW_video_colocalization/solvers/solver_images.m: -------------------------------------------------------------------------------- 1 | function y = solver_images(x, ids) 2 | % x : N vector 3 | % ids: Nimg x 2 matrix containing the begining and end index of each image 4 | 5 | y = zeros(size(x)); 6 | % for each video [parallelizable] 7 | for i = 1 : size(ids,1) 8 | y( ids(i,1) : ids(i,2) ) = solver_image( x( ids(i,1) : ids(i,2) ) ); 9 | end 10 | 11 | 12 | -------------------------------------------------------------------------------- /FW_video_colocalization/solvers/solver_videos_mex.m: -------------------------------------------------------------------------------- 1 | function y = solver_videos_mex(x, E, id, vi) 2 | % x : N vector 3 | % E : contains the edges (nEdges x 2) 4 | % id: Nvid x 2 matrix containing the begining and end index of each image 5 | 6 | y = zeros(size(x)); 7 | % for each video [parallelizable] 8 | for i = 1 : size(id,1) 9 | y( id(i,1) : id(i,2) ) = solver_video_mex( x( id(i,1) : id(i,2)) , E{i}, vi( id(i,1) : id(i,2),2 )); 10 | end 11 | 12 | -------------------------------------------------------------------------------- /FW_video_colocalization/solvers/solver_videos.m: -------------------------------------------------------------------------------- 1 | function y = solver_videos(x, C, ids, var_index) 2 | % x : N vector 3 | % C is a Nvid cell containing sparse adjency matrix such tthat C(i,j) = 1 if the is an edge i->j 4 | % ids: Nvid x 2 matrix containing the begining and end index of each image 5 | 6 | y = zeros(size(x)); 7 | % for each video [parallelizable] 8 | for i = 1 : size(ids,1) 9 | y( ids(i,1) : ids(i,2) ) = solver_video( x( ids(i,1) : ids(i,2) ) , C{i}, var_index( ids(i,1) : ids(i,2),2 )); 10 | end 11 | -------------------------------------------------------------------------------- /FW_video_colocalization/solvers/solver_images_epic.m: -------------------------------------------------------------------------------- 1 | function y = solver_images_epic(x, ids) 2 | % x : N vector 3 | % ids: Nimg x 2 matrix containing the begining and end index of each image 4 | persistent idx 5 | 6 | if ~exist('idx','var') 7 | idx = ([1:(numel(y) / 20)]-1) * 20; 8 | end 9 | 10 | y = zeros(size(x)); 11 | 12 | % fast reshape 13 | [~, b] = min(reshape(x, 20, [])); 14 | y(b + idx) = 1; 15 | 16 | %{ 17 | % for each video [parallelizable] 18 | for i = 1 : size(ids,1) 19 | y( ids(i,1) : ids(i,2) ) = solver_image( x( ids(i,1) : ids(i,2) ) ); 20 | end 21 | %} 22 | 23 | -------------------------------------------------------------------------------- /triangle_FW_experiment/processOptions.m: -------------------------------------------------------------------------------- 1 | function options = processOptions(optionsInput, optionsDefault) 2 | 3 | options = optionsDefault; 4 | if (isstruct(optionsInput)) 5 | fields = fieldnames(optionsInput); 6 | for i=1:length(fields) 7 | if (~isstruct(getfield(optionsInput, fields{i}))) 8 | options = setfield(options, fields{i}, getfield(optionsInput, fields{i})); 9 | else 10 | temp = processOptions(getfield(optionsInput, fields{i}), ... 11 | getfield(options, fields{i}) ); 12 | options = setfield(options, fields{i}, temp); 13 | end 14 | end 15 | end 16 | 17 | end % processOptions 18 | -------------------------------------------------------------------------------- /FW_video_colocalization/solvers/init_images.m: -------------------------------------------------------------------------------- 1 | function [x_0, S_0, alpha_0, fun_optim, ids] = init_images(var_index) 2 | 3 | N = size(var_index,1); 4 | [~,ie] = unique(var_index(:,1:2), 'rows','last'); 5 | [~,ib] = unique(var_index(:,1:2), 'rows','first'); 6 | ids = [ib, ie]; 7 | 8 | fun_optim = @(x) solver_images(x, ids); 9 | 10 | % initialization: 11 | % WARNING the active set and their weights has to be intialize to 12 | % such that x_0 = S_0 * alpha_0'; 13 | n_imgs = size(ids,1); 14 | boxes_per_img = N / n_imgs; 15 | x_0 = ones(N,1) / boxes_per_img; 16 | S_0 = zeros(N,boxes_per_img); 17 | alpha_0 = ones(1, boxes_per_img) / boxes_per_img; 18 | for i = 1 : boxes_per_img 19 | S_0(ids(:,1) + (i-1) * (N + 1)) = 1; 20 | end 21 | -------------------------------------------------------------------------------- /FW_video_colocalization/solvers/init_videos.m: -------------------------------------------------------------------------------- 1 | function [x_0, S_0, alpha_0, fun_optim, ids] = init_videos(var_index, C, type) 2 | 3 | N = size(var_index,1); 4 | [~,ie] = unique(var_index(:,1), 'rows','last'); 5 | [~,ib] = unique(var_index(:,1), 'rows','first'); 6 | ids = [ib, ie]; 7 | 8 | if ~exist('type','var') || ~strcmp(type, 'mex') 9 | fun_optim = @(x) solver_videos(x, C, ids, var_index); 10 | else 11 | V = cell(size(C)); 12 | for c = 1 : numel(C) 13 | [i,j,~] = find(C{c}); 14 | V{c} = [i,j] - 1; 15 | if isempty(V{c}) 16 | V{c} = -ones(1,2); 17 | end 18 | V{c} = int32(V{c}); 19 | end 20 | ids = int32(ids); 21 | var_index = int32(var_index); 22 | fun_optim = @(x) solver_videos_mex(x, V, ids, var_index - 1); 23 | end 24 | 25 | %x_0 = fun_optim(-rand(N,1)); 26 | x_0 = fun_optim(-ones(N,1)); 27 | 28 | S_0 = x_0; 29 | alpha_0 = 1; 30 | 31 | -------------------------------------------------------------------------------- /FW_video_colocalization/solvers/solver_video.m: -------------------------------------------------------------------------------- 1 | function y = solver_video(x, C, id_frame) 2 | % x is a N vector representing each boxes in each frame of the video 3 | % C is an NxN sparse adjency matrix such tthat C(i,j) = 1 if the is an edge i->j 4 | 5 | N = size(x,1); 6 | y = zeros(N,1); 7 | % WARNING: 8 | scores = x; 9 | argmin_score = N; 10 | parents = zeros(N,1); 11 | 12 | frame_end = id_frame(end); 13 | 14 | 15 | % construct the paths 16 | for i = 1 : N 17 | parents_of_i = find( C(:,i) ); 18 | 19 | if ~isempty(parents_of_i) 20 | [s, p] = min( scores(parents_of_i) ); 21 | parents(i) = parents_of_i(p); 22 | scores(i) = scores(i) + s; 23 | 24 | % WARNING should take the argmin/argmin only on the last frame? 25 | if id_frame(i) == frame_end && (~exist('min_score','var') || scores(i) < min_score) 26 | min_score = scores(i); 27 | argmin_score = i; 28 | end 29 | end 30 | end 31 | 32 | % retrieve the best path: 33 | while argmin_score ~= 0 34 | %fprintf('n = %d - score = %f\n', argmin_score, scores(argmin_score)); 35 | y(argmin_score) = 1; 36 | argmin_score = parents(argmin_score); 37 | end 38 | 39 | -------------------------------------------------------------------------------- /FW_video_colocalization/COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Armand Joulin, Kevin Tang 2 | All rights reserved. 3 | [for the solvers & data folders] 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER ''AS IS'' AND ANY 14 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER BE LIABLE FOR ANY 17 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /FW_lasso/run_FW.m: -------------------------------------------------------------------------------- 1 | %% Script to reproduce the Lasso experiment in the paper: 2 | % 3 | % On the Global Linear Convergence of Frank-Wolfe Optimization Variants, 4 | % Simon Lacoste-Julien and Martin Jaggi, NIPS 2015. 5 | % 6 | % It reproduces top of Figure 2 and takes a few seconds to run on my 7 | % laptop. 8 | % 9 | % minimize ||A*x -b||^2 s.t. x is in l1-ball of radius r 10 | 11 | 12 | clear 13 | 14 | 15 | %% random test case 16 | d=200; % # rows in A 17 | n=500; % dimension of w = # columns of A = # variables in x 18 | t=25; % 2t = number of non-zeros in the true solution xstar 19 | rng('default'); rng(42); % you need to outcomment this line if running in octave 20 | A=randn(d,n); 21 | xstar=[ones(t,1); -ones(t,1); -zeros(n-2*t,1)]; 22 | noise=0.1*randn(d,1); 23 | b=A*xstar+noise; 24 | one_norm_of_xstar = norm(xstar,1) 25 | 26 | r = 20; % the regularization constraint imposed on the l_1-norm 27 | 28 | 29 | 30 | Aall = r*horzcat(A,-A); % all 2n atoms, over the simplex 31 | 32 | %% 33 | opts.Tmax = 1000; % max number of iterations 34 | opts.TOL = 1e-8; % tolerance for convergence 35 | opts.verbose = true; 36 | opts.pureFW = 1; % to not use away steps... 37 | 38 | %% running FW: 39 | [x_t,f_t, resFW] = FW(Aall, b, opts); 40 | 41 | %% running AFW: 42 | opts.pureFW = 0; 43 | [x_t,f_t, resAFW] = FW(Aall, b, opts); 44 | 45 | %% running pairwise FW: 46 | opts.pureFW = 0; 47 | [x_t,f_t, resPairFW] = FW_pair(Aall, b, opts); 48 | 49 | %% plotting results: 50 | 51 | figure 52 | semilogy(resFW.gap,'b'); 53 | hold on 54 | semilogy(resAFW.gap, 'k'); 55 | hold on 56 | semilogy(resPairFW.gap, 'r'); 57 | legend({'FW', 'awayFW', 'pairFW'}); 58 | 59 | 60 | %% 61 | xt = get(gca, 'XTick'); set(gca, 'FontSize', 16) 62 | xlabel('iteration','FontSize',20) 63 | ylabel('gap','FontSize',20) 64 | 65 | 66 | -------------------------------------------------------------------------------- /FW_video_colocalization/README: -------------------------------------------------------------------------------- 1 | There are already pre-compiled versions of the mex files in the solvers 2 | directory for Linux, Mac and Windows 64 bits, so the code might run 3 | fine as is in Matlab. Otherwise, you need first to compile the mex files: 4 | 1) Go to the solvers directory, run "mex solver_video_mex.cpp" 5 | 6 | Then to launch the script, go back to this folder and: 7 | 2) run "run_FW.m" 8 | 9 | *** 10 | Code for image and video colocalization. 11 | 12 | This code uses the LMO code for shortest path in a network, provided by Armand Joulin and Kevin Tang. 13 | Copyright (c) 2014, Armand Joulin, Kevin Tang 14 | obtained from: http://ai.stanford.edu/~ajoulin/code/FW.zip 15 | (the solvers and data directory). 16 | 17 | If you use this code please cite: 18 | 19 | @inproceedings{JouTangFeiECCV14, 20 | title = {Efficient Image and Video Co-localization with Frank-Wolfe Algorithm}, 21 | author = {Armand Joulin and Kevin Tang and Li Fei-Fei}, 22 | year = {2014}, 23 | booktitle = {European Conference on Computer Vision (ECCV)}, 24 | } 25 | 26 | @InProceedings{TangJouFeiCVPR14, 27 | title = "Co-localization in Real-World Images", 28 | booktitle = "Proceedings of the Conference on Computer Vision and Pattern Recognition (CVPR)", 29 | author = "Kevin Tang, Armand Joulin, Li-Jia Li and Li Fei-Fei", 30 | year = "2014" 31 | } 32 | 33 | The AFW.m function was based on their code, but then modified using a hashing 34 | function for the active set to be more efficient. 35 | 36 | The FW, PFW and AFW code were modified by: 37 | Simon Lacoste-Julien and Martin Jaggi 38 | @inproceedings{lacosteNIPS15fw, 39 | title = {On the Global Linear Convergence of {F}rank-{W}olfe Optimization Variants}, 40 | author = {Simon Lacoste-Julien and Martin Jaggi}, 41 | year = {2015}, 42 | booktitle = {NIPS}, 43 | } 44 | -------------------------------------------------------------------------------- /FW_video_colocalization/run_FW.m: -------------------------------------------------------------------------------- 1 | %% Script to reproduce the flow QP experiment in the paper: 2 | % 3 | % On the Global Linear Convergence of Frank-Wolfe Optimization Variants, 4 | % Simon Lacoste-Julien and Martin Jaggi, NIPS 2015. 5 | % 6 | % It reproduces bottom of Figure 2 and takes less than 1 minute to run on my 7 | % laptop. 8 | % 9 | % minimize 1/2 x' A x + b' x 10 | % s.t. x belongs to some sort of flow polytope 11 | % arising in the video co-localization application 12 | % described in: 13 | % Efficient Image and Video Co-localization with Frank-Wolfe Algorithm, 14 | % Armand Joulin, Kevin Tang and Li Fei-Fei, ECCV 2014 15 | % -- their original code: http://ai.stanford.edu/~ajoulin/code/FW.zip 16 | 17 | 18 | clear 19 | 20 | addpath solvers 21 | addpath data 22 | 23 | %% 24 | 25 | is_imgs = false; % true : images / false : videos 26 | is_test = true; 27 | 28 | load('aeroplane_data_small.mat'); 29 | 30 | % get the LMO: 31 | % linear function to optimize 32 | % i.e. f(x) = argmin_{v in A} 33 | if is_imgs 34 | [x_0, S_0, alpha_0, fun_optim, ids] = init_images(var_index); 35 | else 36 | [x_0, S_0, alpha_0, fun_optim, ids] = init_videos(var_index, edge_index,'mex'); 37 | end 38 | 39 | 40 | %% 41 | opts.Tmax = 2000; % max number of iteration 42 | opts.TOL = 1e-8; % tolerance for convergence 43 | opts.verbose = true; 44 | 45 | %% running FW: 46 | [x_t,f_t, resFW] = FW(x_0, A, b, fun_optim, opts); 47 | 48 | %% running AFW: 49 | [x_t,f_t, resAFW] = AFW(x_0, S_0, alpha_0, A, b, fun_optim, opts); 50 | 51 | %% running pairwise FW: 52 | opts.pureFW = 0; 53 | [x_t,f_t, resPairFW] = PFW(x_0, S_0, alpha_0, A, b, fun_optim, opts); 54 | 55 | %% plotting results: 56 | 57 | figure 58 | semilogy(resFW.gap,'b'); 59 | hold on 60 | semilogy(resAFW.gap, 'k'); 61 | hold on 62 | semilogy(resPairFW.gap, 'r'); 63 | legend({'FW', 'awayFW', 'pairFW'}); 64 | 65 | 66 | %% 67 | xt = get(gca, 'XTick'); set(gca, 'FontSize', 16) 68 | xlabel('iteration','FontSize',20) 69 | ylabel('gap','FontSize',20) 70 | 71 | -------------------------------------------------------------------------------- /triangle_FW_experiment/additional_experiment.m: -------------------------------------------------------------------------------- 1 | % Extra exploratory experiments for Appendix E (not in paper) 2 | % just making sure that the theoretical bound is correct. 3 | 4 | % suppose triangle with point at [-1,0],[0,0] and [cos(theta),sin(theta)] 5 | % -> the smaller theta, the worse the width 6 | % % width = sin(theta/2) 7 | % diameter^2 = (1+cos(theta))^2+ sin(theta)^2 = 2*(1+cos(theta)) 8 | % or also: diameter = 2 cos(theta/2) 9 | % so constant in rate is: 10 | % width^2/diameter^2 = 1/4*(tan(theta/2))^2 11 | % 12 | % Note: by looking instead at ()^2 angle (tighter constant) 13 | % we gain a factor of 4: it should just be (sin(theta/2))^2 14 | 15 | 16 | % consider 1/2 ||x-x_0||^2 as objective with x_0 = [1/2,0]; 17 | % note: condition number of 1 18 | 19 | xstar = [-0.5; 0]; 20 | 21 | grad = @(x) (x-xstar); 22 | f = @(x) 0.5*norm(x-xstar)^2; 23 | 24 | clear opts 25 | 26 | opts.maxK = 500; 27 | 28 | opts.TOL = 1e-5; 29 | 30 | %% 31 | theta = pi/50; 32 | 33 | constant = 1/4*(tan(theta/2))^2; 34 | 35 | V = [0,0; -1, 0; cos(theta), sin(theta)]'; % vertices for polytope 36 | 37 | % drawing triangle: 38 | figure 39 | edges = [2,1; 1,3; 2,3]; 40 | for i = 1:3 41 | e = edges(i,:); 42 | plot(V(1,e), V(2,e),'-pb'); 43 | hold on 44 | end 45 | 46 | %% 47 | 48 | opts.pureFW = 1; rFW = AFW(V, grad, f, opts); 49 | opts.pureFW = 0; rAFW = AFW(V, grad, f, opts); 50 | rpFW = pFW(V, grad, f, opts); 51 | 52 | %% 53 | plot(rFW.X(1,1:50), rFW.X(2,1:50),'x--g') 54 | if size(rAFW.X,2) < 50 55 | ind_end = size(rAFW.X,2); 56 | else 57 | ind_end = 50; 58 | end 59 | plot(rAFW.X(1,1:ind_end), rAFW.X(2,1:ind_end),'x--r') 60 | 61 | if size(rpFW.X,2) < 50 62 | ind_end = size(rpFW.X,2); 63 | else 64 | ind_end = 50; 65 | end 66 | plot(rpFW.X(1,1:ind_end), rpFW.X(2,1:ind_end),'s:k') 67 | 68 | %% 69 | figure 70 | semilogy(rFW.primal, 'g') 71 | hold on 72 | semilogy(rAFW.primal, 'r') 73 | semilogy(rpFW.primal, 'k') 74 | legend({'FW', 'AFW', 'pFW'}) 75 | 76 | %% comparing ratio of improvement vs. theory: 77 | 78 | ratio = rpFW.primal(2:end) ./ rpFW.primal(1:(end-1)); 79 | ratio_theo = 1-constant; 80 | figure 81 | plot(ratio./ratio_theo) 82 | title('for pFW: h_{k+1}/h_k divided by (1-\rho) [should be <= 1]') 83 | 84 | % AFW: 85 | ratio = rAFW.primal(2:end) ./ rAFW.primal(1:(end-1)); 86 | ratio_theo = 1-constant/4; 87 | figure 88 | plot(ratio./ratio_theo) 89 | title('for AFW: h_{k+1}/h_{k} divided by (1-\rho/4) [should be <= 1]') 90 | -------------------------------------------------------------------------------- /FW_video_colocalization/FW.m: -------------------------------------------------------------------------------- 1 | function [x_t,f_t,res] = FW(x_0, A, b, fun_optim, opts) 2 | % [x_t,f_t,res] = FW(x_0, S_0, alpha_0, A, b, fun_optim, opts) 3 | % -> res: objective tracking 4 | % 5 | % This code runs standard FW 6 | % (so no need to maintain active set). 7 | % 8 | % 9 | % objective is: 0.5 x' A x + b' x 10 | % 11 | % [x_t, f_t] = FW(x_0, S_0, alpha_0, A, fun_optim, opts) 12 | % x_0 : variable initialization 13 | % A : the matrix in the cost function of the QP 14 | % fun_optim : the linear minimization oracle function to solve the related LP: 15 | % min_{z in M} 16 | % for images : take the min of the integer solutions (eg z=(1,0,0) ) 17 | % for videos : shortest path algorithm 18 | % 19 | % opts.Tmax % max number of iterations 20 | % opts.TOL % tolerance for convergence (FW gap stopping criterion) 21 | % opts.verbose % whether to print progress 22 | % 23 | % res.primal = fvalues; 24 | % res.gap = gap_values; 25 | % res.x_t = x_t; 26 | 27 | it = 1; 28 | minf = 0; 29 | minx = []; 30 | % init: 31 | 32 | x_t = x_0; 33 | 34 | % tracking results: 35 | 36 | fvalues = []; 37 | gap_values = []; 38 | 39 | fprintf('running plain FW, for at most %d iterations\n', opts.Tmax); 40 | 41 | cost_fun =@(y) .5 * y' *A * y + b' * y; 42 | % optimization: 43 | 44 | while it <= opts.Tmax 45 | it = it + 1; 46 | 47 | % gradient: 48 | grad = A * x_t + b; 49 | 50 | % cost function: 51 | f_t = cost_fun(x_t); 52 | 53 | % towards direction search: 54 | s_FW = fun_optim( grad ); % the linear minimization oracle 55 | d_FW = s_FW - x_t; 56 | 57 | % duality gap: 58 | gap = - d_FW' * grad; 59 | 60 | fvalues(it-1) = f_t; 61 | gap_values(it-1) = gap; 62 | 63 | if opts.verbose 64 | fprintf('it = %d - f = %g - gap=%g\n', it, f_t, gap); 65 | end 66 | 67 | if gap < opts.TOL 68 | fprintf('end of FW: reach small duality gap (gap=%g)\n', gap); 69 | break 70 | end 71 | 72 | % construct direction 73 | 74 | d = d_FW; 75 | max_step = 1; 76 | 77 | % line search: 78 | 79 | step = - (grad' * d) / ( d' * A * d ); 80 | step = max(0, min(step, max_step )); 81 | 82 | if step < -eps 83 | % not a descent direction??? 84 | fprintf('ERROR -- not descent direction???') 85 | keyboard 86 | end 87 | 88 | % FW step: 89 | x_t = x_t + step * d; 90 | 91 | end 92 | 93 | res.primal = fvalues; 94 | res.gap = gap_values; 95 | res.x_t = x_t; 96 | 97 | end 98 | -------------------------------------------------------------------------------- /triangle_FW_experiment/pFW.m: -------------------------------------------------------------------------------- 1 | function res = pFW(V, grad, f, opts) 2 | % function res = pFW(V, grad, f, opts); 3 | % pairwise FW 4 | % 5 | % V(:,i) -> i^th vertex 6 | % 7 | % opts.pureFW=1 to get no away step (0 by default) 8 | % .maxK -> max number of iterations (500 by default) 9 | % .TOL -> stopping criterion on gap (1e-5 by default) 10 | % .seed -> seed for random starting point; set to 0 to use a deterministic 11 | % one; (0 by default) 12 | % 13 | % xstar = [-0.5; 0]; 14 | % 15 | % grad = @(x) (x-xstar); 16 | % f = @(x) 0.5*norm(x-xstar)^2; 17 | 18 | opt_default = defaultOptions(); 19 | if (nargin > 3) 20 | o = processOptions(opts, opt_default); 21 | else 22 | o = opt_default; 23 | end 24 | 25 | 26 | %alpha0 = ones(size(V,2),1)/size(V,2); -> uniform starting point 27 | 28 | if o.seed == 0 29 | alpha0 = ones(size(V,2),1); 30 | alpha0(end) = alpha0(end)*5; 31 | alpha0 = alpha0 / sum(alpha0); 32 | else 33 | rng(o.seed) % you need to change this line if running in octave 34 | alpha0 = rand(size(V,2),1); 35 | alpha0 = alpha0 / sum(alpha0); 36 | end 37 | x0 = V*alpha0; 38 | 39 | MAXK = o.maxK; 40 | 41 | TOL = o.TOL; 42 | 43 | x = x0; 44 | alpha = alpha0; 45 | 46 | drop_finished = 0; 47 | 48 | for k = 1:MAXK 49 | 50 | g_k = grad(x); 51 | 52 | % FW corner: 53 | [~, i_FW] = min(g_k'*V); 54 | 55 | % away corner: 56 | [~, i_A] = max(g_k'*V); 57 | 58 | d_FW = V(:,i_FW)-x; 59 | d_k = V(:,i_FW) - V(:,i_A); 60 | 61 | gap = -g_k'*d_FW; 62 | gap_dir = -g_k'*d_k; 63 | 64 | X(:,k) = x; 65 | G(k) = gap; 66 | F(k) = f(x); 67 | 68 | gamma_max = alpha(i_A); 69 | 70 | gamma_opt = gap_dir / norm(d_k)^2; 71 | 72 | if gamma_opt >= gamma_max 73 | % drop step 74 | % gamma_opt = gamma_max; 75 | % fprintf('DROP STEP!\n'); 76 | % k 77 | % keyboard 78 | % note that because we are on triangle, this drop step means we will 79 | % converge next step... 80 | fprintf('DROP step after %d iterations.\n', k); 81 | drop_finished = 1; 82 | break 83 | end 84 | 85 | if gap < TOL 86 | fprintf('CONVERGED in %d iterations.\n', k); 87 | break 88 | end 89 | 90 | gamma = gamma_opt; 91 | x = x + gamma*d_k; 92 | 93 | alpha(i_FW) = alpha(i_FW) + gamma; 94 | alpha(i_A) = alpha(i_A) - gamma; 95 | 96 | end 97 | 98 | res.X = X; 99 | res.gap = G; 100 | res.primal = F; 101 | res.x = x; 102 | res.alpha = alpha; 103 | res.drop_finished = drop_finished; % means early stopping... 104 | 105 | end 106 | 107 | function options = defaultOptions() 108 | 109 | options = []; 110 | options.maxK = 500; 111 | options.TOL = 1e-5; 112 | options.seed = 0; 113 | 114 | end % defaultOptions 115 | -------------------------------------------------------------------------------- /FW_video_colocalization/solvers/solver_video_mex.cpp: -------------------------------------------------------------------------------- 1 | #include "mex.h" 2 | #include "mexUtil.h" 3 | #include 4 | 5 | using namespace std; 6 | 7 | 8 | void mexFunction( int nlhs, mxArray *plhs[], 9 | int nrhs, const mxArray*prhs[] ) 10 | { 11 | 12 | 13 | enum{ nodes_weight_i, edges_i, idframes_i };//inputs 14 | enum{ ass_o };//output 15 | 16 | myCheckArgNumber(nrhs, 3,nlhs,1);//check nb input / nb output 17 | 18 | /*************************************************************** 19 | * INPUT 20 | ***************************************************************/ 21 | 22 | int nNodes;// nb of nodes 23 | double* nodes_weight = (double*) myCheckArg(prhs, nodes_weight_i, &nNodes, 1, myDouble); 24 | 25 | int nEdges;// number of edges 26 | int* edges = (int*) myCheckArg(prhs, edges_i, &nEdges, 2, myInt); 27 | 28 | // id of frame containing nodes 29 | int* idframes = (int*) myCheckArg(prhs, idframes_i, &nNodes, 1, myInt); 30 | 31 | /*************************************************************** 32 | * INIT SHORTEST PATH 33 | ***************************************************************/ 34 | 35 | // assignement // TODO: change it to bit vector 36 | int* parents = new int[nNodes]; 37 | double* scores = new double[nNodes]; 38 | 39 | memcpy(scores, nodes_weight, sizeof(double) * nNodes); // init scores with weights 40 | 41 | int last_frame = idframes[nNodes-1];// last frame 42 | 43 | /*************************************************************** 44 | * SHORTEST PATH 45 | ***************************************************************/ 46 | 47 | int n, e = 0, it, p, pm, argmin = -1; 48 | double sm, min_score; 49 | bool last_frame_visited = false; 50 | for(n = 0; n < nNodes; n++){ 51 | 52 | while(e < nEdges && edges[e + nEdges] < n) e++; 53 | 54 | bool visited = false; 55 | while(edges[e + nEdges] == n){ 56 | p = edges[e]; 57 | if( !visited || scores[p] < sm){ 58 | pm = p; 59 | sm = scores[p]; 60 | visited = true; 61 | } 62 | e++; 63 | } 64 | 65 | 66 | if(visited){ 67 | parents[n] = pm; 68 | scores[n] += sm; 69 | } 70 | else{ 71 | parents[n] = -1; 72 | } 73 | //printf("n = %d - score = %f - frame= %d\n", n, scores[n]); 74 | 75 | if( idframes[n] == last_frame && (!last_frame_visited || scores[n] < min_score) ){ 76 | min_score = scores[n]; 77 | argmin = n; 78 | last_frame_visited = true; 79 | } 80 | 81 | } 82 | 83 | // retrieve best path: 84 | plhs[ass_o] = mxCreateDoubleMatrix(nNodes, 1, mxREAL); 85 | double* ass = (double*) mxGetPr(plhs[ass_o]); 86 | memset(ass, 0, nNodes * sizeof(double)); 87 | 88 | n = argmin; 89 | it = 0; 90 | while(n != -1 && it < nNodes) 91 | { 92 | //printf("n = %d - score = %f\n", n+1, scores[n]); 93 | ass[n] = 1; 94 | n = parents[n]; 95 | it++; 96 | } 97 | 98 | 99 | delete parents; 100 | delete scores; 101 | } 102 | -------------------------------------------------------------------------------- /triangle_FW_experiment/AFW.m: -------------------------------------------------------------------------------- 1 | function res = AFW(V, grad, f, opts) 2 | % function res = AFW(V, grad, f, opts); 3 | % 4 | % V(:,i) -> i^th vertex 5 | % 6 | % opts.pureFW=1 to get no away step (0 by default) 7 | % .maxK -> max number of iterations (500 by default) 8 | % .TOL -> stopping criterion on gap (1e-5 by default) 9 | % .seed -> seed for random starting point; set to 0 to use a deterministic 10 | % one; (0 by default) 11 | % 12 | % xstar = [-0.5; 0]; 13 | % 14 | % grad = @(x) (x-xstar); 15 | % f = @(x) 0.5*norm(x-xstar)^2; 16 | 17 | opt_default = defaultOptions(); 18 | if (nargin > 3) 19 | o = processOptions(opts, opt_default); 20 | else 21 | o = opt_default; 22 | end 23 | 24 | %alpha0 = ones(size(V,2),1)/size(V,2); -> uniform starting point 25 | 26 | if o.seed == 0 27 | alpha0 = ones(size(V,2),1); 28 | alpha0(end) = alpha0(end)*5; 29 | alpha0 = alpha0 / sum(alpha0); 30 | else 31 | rng(o.seed) % you need to change this line if running in octave 32 | alpha0 = rand(size(V,2),1); 33 | alpha0 = alpha0 / sum(alpha0); 34 | end 35 | x0 = V*alpha0; 36 | 37 | MAXK = o.maxK; 38 | 39 | TOL = o.TOL; 40 | 41 | x = x0; 42 | alpha = alpha0; 43 | 44 | naway = 0; 45 | drop_finished = 0; 46 | 47 | for k = 1:MAXK 48 | 49 | g_k = grad(x); 50 | 51 | % FW corner: 52 | [~, i_FW] = min(g_k'*V); 53 | s = V(:,i_FW); 54 | d_FW = s-x; 55 | 56 | % away corner: 57 | [~, i_A] = max(g_k'*V); 58 | v = V(:,i_A); 59 | d_A = x-v; 60 | 61 | gap = -g_k'*d_FW; 62 | g_A = -g_k'*d_A; 63 | 64 | X(:,k) = x; 65 | G(k) = gap; 66 | GA(k) = g_A; 67 | F(k) = f(x); 68 | 69 | if gap < TOL 70 | fprintf('CONVERGED in %d iterations.\n', k); 71 | break 72 | end 73 | 74 | if gap > g_A || o.pureFW 75 | %FW step: 76 | isFW = 1; 77 | d_k = d_FW; 78 | gamma_max = 1; 79 | else 80 | naway = naway+ 1; 81 | % away step: 82 | isFW = 0; 83 | d_k = d_A; 84 | gamma_max = alpha(i_A) / (1-alpha(i_A)); 85 | end 86 | 87 | gap_dir = -g_k'*d_k; 88 | 89 | gamma_opt = gap_dir / norm(d_k)^2; 90 | 91 | if gamma_opt >= gamma_max 92 | % drop step 93 | % gamma_opt = gamma_max; 94 | % fprintf('DROP STEP!\n'); 95 | % k 96 | % keyboard 97 | % note that because we are on triangle, this drop step means we will 98 | % converge next step... 99 | fprintf('DROP step after %d iterations.\n', k); 100 | drop_finished = 1; 101 | break 102 | end 103 | 104 | gamma = gamma_opt; 105 | x = x + gamma*d_k; 106 | 107 | if isFW 108 | alpha = (1-gamma)*alpha; 109 | alpha(i_FW) = alpha(i_FW) + gamma; 110 | else 111 | alpha = (1+gamma)*alpha; 112 | alpha(i_A) = alpha(i_A) - gamma; 113 | end 114 | end 115 | 116 | res.X = X; 117 | res.gap = G; 118 | res.primal = F; 119 | res.gap_A = GA; 120 | res.x = x; 121 | res.alpha = alpha; 122 | res.naway = naway; 123 | res.drop_finished = drop_finished; % means early stopping... 124 | 125 | end 126 | 127 | function options = defaultOptions() 128 | 129 | options = []; 130 | options.pureFW = 0; 131 | options.maxK = 500; 132 | options.TOL = 1e-5; 133 | options.seed = 0; 134 | 135 | end % defaultOptions 136 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #LinearFW 2 | 3 | This is the code to reproduce all the experiments in our 4 | [NIPS 2015 paper](http://arxiv.org/pdf/1511.05932v1): 5 | ``` 6 | On the Global Linear Convergence of Frank-Wolfe Optimization Variants 7 | Simon Lacoste-Julien and Martin Jaggi 8 | NIPS 2015 9 | ``` 10 | which covers the global linear convergence rate of Frank-Wolfe 11 | optimization variants for problems described as in Eq. (1) in the paper. 12 | It contains the implementation of Frank-Wolfe, 13 | away-steps Frank-Wolfe and pairwise Frank-Wolfe on two applications: 14 | 15 | 1. l1-constrained least-square regression (lasso); 16 | 2. a QP on the flow polytope coming from a video co-localization application. 17 | 18 | The code runs in Matlab (was tested in Matlab 2014 on Linux, Windows and Mac). 19 | But for the first two folders below, it also runs in Octave easily by 20 | removing the line initializing the random seed. 21 | 22 | There are three folders: 23 | * `FW_lasso` contains the Lasso experiment to produce the top figure in 24 | Figure 2. Launch `run_FW.m` in the folder to produce the plot (takes a few seconds). 25 | * `triangle_FW_experiment` contains the empirical tigthness of the linear rate 26 | constant experiment, from Appendix E (Figure 5). Launch `run_triangles.m` 27 | in the folder to produce the plots (takes about 30 seconds). 28 | * `FW_video_colocalization` containts the video co-localization QP experiment 29 | to produce the bottom figure in Figure 2. Launch `run_FW.m` in the folder 30 | to produce the plot (takes less than 1 minute). If you get the 31 | `Undefined function 'solver_video_mex'` error, you need to mex the following 32 | file to get the correct LMO; go to the `solvers` subfolder, and then 33 | run `mex solver_video_mex.cpp` in it. 34 | 35 | ##Credits 36 | 37 | The video co-localization code was written by 38 | [Armand Joulin](http://ai.stanford.edu/~ajoulin/) and 39 | [Kevin Tang](http://ai.stanford.edu/~kdtang/). 40 | We obtained it [here](http://ai.stanford.edu/~ajoulin/code/FW.zip) 41 | and modified the Frank-Wolfe code by adding a hashing function 42 | to make the active set maintenance more efficient, as well as 43 | added the pairwise FW variant. Their 44 | video co-localization approach is described in the paper: 45 | ``` 46 | Efficient Image and Video Co-localization with Frank-Wolfe Algorithm 47 | Armand Joulin, Kevin Tang and Li Fei-Fei 48 | ECCV 2014 49 | ``` 50 | 51 | ##Extending the code 52 | 53 | You can easily re-use the code in the `FW_video_colocalization` folder 54 | to adapt it to other QPs with different domains. For this, you mainly need 55 | to modify two things: 56 | 57 | 1. You need to implement your own Linear Minimization Oracle (LMO) on your 58 | domain. You pass it as the `fun_optim` argument to the FW functions 59 | (`FW` for standard FW; `AFW` for away-steps FW and `PFW` for pairwise 60 | FW). This function takes a vector as argument and returns an atom 61 | that minimizes the dot product with this vector over the domain. 62 | 2. If your atoms are something different than just 0-1 vectors, then 63 | you also need to modify the `hashing` internal function for `AFW` 64 | and `PFW` so that you can properly encode the atoms in your domain 65 | in a unique string (or number) (this is used for the efficient 66 | maintenance of the active set). The current hashing function only 67 | supports 0-1 vectors. 68 | 69 | ##Disclaimer 70 | 71 | This code is not meant to be the most efficient. For example, an implementation 72 | handling the sparsity of the atoms could be faster for the video 73 | co-localization application. Our goal was to simply demonstrate how the FW 74 | variants work on practical applications. 75 | 76 | ##Citation 77 | 78 | Please use the following BibTeX entry to cite this software in your work: 79 | 80 | @inproceedings{LacosteJulien2015linearFW, 81 | author = {Simon Lacoste-Julien and Martin Jaggi}, 82 | title = {On the Global Linear Convergence of {F}rank-{W}olfe Optimization Variants}, 83 | booktitle = {Advances in Neural Information Processing Systems (NIPS)}, 84 | year = {2015}, 85 | } 86 | 87 | And if you use the video co-localization LMO, you also need to cite: 88 | 89 | @inproceedings{JouTangFeiECCV14, 90 | title = {Efficient Image and Video Co-localization with {F}rank-{W}olfe Algorithm}, 91 | author = {Armand Joulin and Kevin Tang and Li Fei-Fei}, 92 | booktitle = {European Conference on Computer Vision (ECCV)}, 93 | year = {2014}, 94 | } 95 | 96 | 97 | ##Authors 98 | 99 | * [Simon Lacoste-Julien](http://www.di.ens.fr/~slacoste/) 100 | * [Martin Jaggi](http://people.inf.ethz.ch/jaggim/) 101 | -------------------------------------------------------------------------------- /FW_lasso/FW_pair.m: -------------------------------------------------------------------------------- 1 | function [x_t,f_t,res] = FW_pair(A, b, opts) 2 | % [x_t,f_t,res] = FW_pair(A, b, opts) 3 | % -> res: objective tracking 4 | % 5 | % solves min ||x-b|| with x constrained to a finite atomic norm ball (here 6 | % for the lasso, A is the set of scaled lasso columns and their negatives) 7 | % 8 | % This code runs pairwise FW (Algorithm 2 in paper). 9 | % 10 | % opts.Tmax % max number of iterations 11 | % opts.TOL % tolerance for convergence (FW gap stopping criterion) 12 | % opts.verbose % whether to print info 13 | 14 | [d,n] = size(A); % n is the number of atoms here (twice the number of lasso data columns) 15 | 16 | % init: 17 | % alpha_t will be a weight vector so that x_t = S_t * alpha_t 18 | alpha_t = zeros(n,1); alpha_t(1) = 1; 19 | x_t = A(:,1); % this is tracking A\alpha in the lasso case 20 | 21 | 22 | % each column of S_t = A(:,I_active) is a potential vertex 23 | % I_active -> contains index of active vertices (same as alpha_t > 0) 24 | % alpha_t(i) == 0 implies vertex is not active anymore... 25 | % alpha_t will be a weight vector so that x_t = A * alpha_t 26 | I_active = find(alpha_t > 0); 27 | 28 | % tracking results: 29 | 30 | fvalues = []; 31 | gap_values = []; 32 | number_drop = 0; % counting drop steps (max stepsize for away step) 33 | 34 | fprintf('running pairwise FW, for at most %d iterations\n', opts.Tmax); 35 | 36 | % optimization: 37 | it = 1; 38 | while it <= opts.Tmax 39 | it = it + 1; 40 | 41 | % cost function: 42 | f_t = objective_fun(x_t,b); 43 | % gradient = x-b: 44 | grad = grad_fun(x_t,b); 45 | 46 | % towards direction search: 47 | [id_FW, s_FW] = LMO(A,grad); % the linear minimization oracle, returning an atom 48 | 49 | d_FW = s_FW - x_t; 50 | 51 | % duality gap: 52 | gap = - d_FW' * grad; 53 | 54 | fvalues(it-1) = f_t; 55 | gap_values(it-1) = gap; 56 | 57 | if gap < opts.TOL 58 | fprintf('end of FW: reach small duality gap (gap=%g)\n', gap); 59 | break 60 | end 61 | 62 | % away direction search: 63 | [id_A, v_A] = away_step(grad, A, I_active); 64 | d_A = x_t - v_A; 65 | alpha_max = alpha_t(id_A); 66 | 67 | % construct pair direction (between towards and away): 68 | 69 | d = s_FW - v_A; %was for away: d_A; 70 | max_step = alpha_max; % was for away: alpha_max / (1 - alpha_max); 71 | 72 | % line search: (same as away, but different d) 73 | %step = - (grad' * d) / ( d' * A * d ); % was this for the video QP 74 | step = - (grad' * d) / ( d' * d ); 75 | 76 | % simpler predefined stepsize 77 | %stepSize = 2 / (t+2); 78 | step = max(0, min(step, max_step )); 79 | 80 | if opts.verbose 81 | fprintf('it = %d - f = %g - gap=%g - stepsize=%g\n', it, f_t, gap, step); 82 | end 83 | 84 | if step < -eps 85 | % not a descent direction??? 86 | fprintf('ERROR -- not descent direction???') 87 | keyboard 88 | end 89 | 90 | % doing steps and updating active set: 91 | 92 | % away part of the step step: 93 | %alpha_t = (1+step)*alpha_t; % note that inactive should stay at 0; 94 | if abs(step - max_step) < 10*eps 95 | % drop step: 96 | number_drop = number_drop+1; 97 | alpha_t(id_A) = 0; 98 | I_active(I_active == id_A) = []; % remove from active set 99 | else 100 | alpha_t(id_A) = alpha_t(id_A) - step; 101 | end 102 | 103 | % towards part of the step: 104 | % alpha_t = (1-step)*alpha_t; 105 | 106 | alpha_t(id_FW) = alpha_t(id_FW) + step; 107 | 108 | I_active = find(alpha_t > 0); %TODO: could speed up by checking if id_FW is new 109 | 110 | % exceptional case: stepsize of 1, this collapses the active set! 111 | if step > 1-eps; 112 | I_active = [id_FW]; 113 | end 114 | 115 | x_t = x_t + step * d; 116 | 117 | end 118 | 119 | function obj = objective_fun(x,b) 120 | obj = sum((x-b).^2)/2; 121 | %obj = .5 * norm(x - b, 2); %gives a different result 122 | end 123 | function grad = grad_fun(x,b) 124 | grad = x - b; 125 | end 126 | 127 | %LMO returns the index of, and the atom of max inner product with the negative gradient 128 | function [id, s] = LMO(A,grad) 129 | ips = grad' * A; 130 | [~,id] = min(ips); 131 | s = A(:,id); 132 | end 133 | 134 | % returns the id of the active atom with the worst value w.r.t. the 135 | % gradient 136 | function [id, v] = away_step(grad, A, I_active) 137 | ips = grad' * A(:,I_active); 138 | [~,id] = max(ips); 139 | id = I_active(id(1)); 140 | v = A(:,id); 141 | end 142 | 143 | 144 | res.primal = fvalues; 145 | res.gap = gap_values; 146 | res.number_drop = number_drop; 147 | res.S_t = A(:,I_active); 148 | res.alpha_t = alpha_t; 149 | res.x_t = x_t; 150 | 151 | end 152 | -------------------------------------------------------------------------------- /triangle_FW_experiment/run_triangles.m: -------------------------------------------------------------------------------- 1 | %% Script to reproduce the experiment of Appendix E in paper: 2 | % 3 | % On the Global Linear Convergence of Frank-Wolfe Optimization Variants, 4 | % Simon Lacoste-Julien and Martin Jaggi, NIPS 2015. 5 | % 6 | % It reproduces Figure 5 on p.23, and takes about 30 seconds to run on my 7 | % laptop. 8 | 9 | % suppose triangle with point at [-1,0],[0,0] and [cos(theta),sin(theta)] 10 | % -> the smaller theta, the worse the width 11 | % % width = sin(theta/2) 12 | % diameter^2 = (1+cos(theta))^2+ sin(theta)^2 = 2*(1+cos(theta)) 13 | % or also: diameter = 2 cos(theta/2) 14 | % so constant in rate is: 15 | % width^2/diameter^2 = 1/4*(tan(theta/2))^2 16 | % 17 | % Note: by looking instead at ()^2 angle (tighter constant) 18 | % we gain a factor of 4: it should just be (sin(theta/2))^2 19 | 20 | 21 | % consider 1/2 ||x-x_0||^2 as objective with x_0 = [1/2,0]; 22 | % note: condition number of 1 23 | 24 | SAVEPLOT = 0; % set to 1 to export the flot (it requires export_fig) 25 | 26 | xstar = [-0.5; 0]; 27 | 28 | grad = @(x) (x-xstar); 29 | f = @(x) 0.5*norm(x-xstar)^2; 30 | 31 | clear opts 32 | 33 | opts.maxK = 2000; 34 | 35 | opts.TOL = 1e-10; 36 | opts.pureFW = 0; 37 | 38 | %% 39 | 40 | thetas = pi./[4,10,20,50,100,200,500,1000,1500,2000]; 41 | 42 | seeds = [5:347:(20*347)]; 43 | 44 | methods = {@AFW, @pFW}; 45 | 46 | for choice = 1:length(thetas) 47 | theta = thetas(choice); 48 | 49 | % theoretical constant: 50 | constant = 1/4*(tan(theta/2))^2; 51 | %tighter version: 52 | %constant = (sin(theta/2))^2; 53 | 54 | V = [0,0; -1, 0; cos(theta), sin(theta)]'; % vertices for polytope 55 | 56 | for meth = 1:2 57 | for i_repeat = 1:length(seeds) 58 | s = seeds(i_repeat); 59 | opts.seed = s; 60 | solver = methods{meth}; 61 | r = solver(V, grad, f, opts); 62 | if r.drop_finished 63 | rates(choice,meth,i_repeat) = nan; 64 | continue 65 | end 66 | fprintf('theta = %1.2g\n', theta) 67 | % estimate empirical linear rate from regression: 68 | y = log(r.primal(10:end)); 69 | x = 1:length(r.primal(10:end)); 70 | p = polyfit(x,y,1); 71 | rates(choice,meth,i_repeat) = -p(1); 72 | end 73 | end 74 | rates(choice,3,:) = constant; % theoretical rate stored in 3rd column 75 | end 76 | 77 | raw_rates = rates; 78 | 79 | %% 80 | for i = 1:size(rates,1) 81 | for j = 1:size(rates,2) 82 | I = find(~isnan(rates(i,j,:))); 83 | new_rates(i,j) = median(rates(i,j,I)); 84 | U(i,j) = quantile(rates(i,j,I),0.75); 85 | L(i,j) = quantile(rates(i,j,I),0.25); 86 | end 87 | end 88 | rates = new_rates; 89 | 90 | U = U-rates; % get difference between max and median... 91 | L = rates - L; 92 | 93 | %% PFW vs. theoretical constant: 94 | figure 95 | hd(2) = semilogx(thetas, rates(:,2)./rates(:,3),'xr-','MarkerSize',10); 96 | hold on 97 | eBar = errorbar(thetas, rates(:,2)./rates(:,3), L(:,2)./rates(:,3), U(:,2)./rates(:,3),'r','LineWidth',3); 98 | 99 | %axis([thetas(end)/2, thetas(1)*2, 0,15]) 100 | 101 | set(gca, 'FontSize', 16) 102 | xlabel('\theta (rad)','FontSize',20) 103 | ylabel('ratio','FontSize',20) 104 | 105 | %title('Ratio of empirical rate constant for PFW to theoretical constant') 106 | 107 | % AFW vs. theoretical constant 108 | %figure 109 | hd(1) = semilogx(thetas, 4*rates(:,1)./rates(:,3),'sk-','MarkerSize',10); 110 | %hold on 111 | % theoretical constant for AFW is 1/4 of the one of PFW: 112 | eBar = errorbar(thetas, 4*rates(:,1)./rates(:,3), 4*L(:,1)./rates(:,3), 4*U(:,1)./rates(:,3),'k','LineWidth',3); 113 | 114 | axis([thetas(end)/2, thetas(1)*2, 0,60]); 115 | 116 | 117 | legend(hd, 'AFW', 'PFW') 118 | grid on 119 | 120 | if SAVEPLOT 121 | export_fig('empirical_vs_theory','-pdf','-transparent'); 122 | else 123 | title('Ratio of empirical rate constant for AFW to theoretical constant') 124 | end 125 | 126 | %% 127 | figure 128 | % comparing PFW vs. AFW: 129 | temp = squeeze(raw_rates(:,2,:)./ raw_rates(:,1,:)); 130 | for i = 1:size(temp,1) 131 | I = find(~isnan(temp(i,:))); 132 | PFW_vs_AFW(i) = median(temp(i,I)); 133 | Ucomparison(i) = quantile(temp(i,I),0.75); 134 | Lcomparison(i) = quantile(temp(i,I),0.25); 135 | end 136 | Ucomparison = Ucomparison-PFW_vs_AFW; 137 | Lcomparison = PFW_vs_AFW-Lcomparison; 138 | 139 | semilogx(thetas, PFW_vs_AFW,'s-b','LineWidth', 3,'MarkerSize',10) 140 | hold on 141 | eBar = errorbar(thetas, PFW_vs_AFW, Lcomparison, Ucomparison,'b','LineWidth',3); 142 | plot(thetas, ones(size(thetas)),'k--','LineWidth', 3) 143 | 144 | set(gca, 'FontSize', 16) 145 | xlabel('\theta (rad)','FontSize',20) 146 | ylabel('ratio of PFW vs. AFW','FontSize',20) 147 | 148 | grid on 149 | 150 | if SAVEPLOT 151 | export_fig('ratio_PFW_vs_AFW','-pdf','-transparent'); 152 | else 153 | title('Ratio of rate for PFW over AFW') 154 | end 155 | 156 | 157 | -------------------------------------------------------------------------------- /FW_lasso/FW.m: -------------------------------------------------------------------------------- 1 | function [x_t,f_t,res] = FW(A, b, opts) 2 | % [x_t,f_t,res] = FW(A, b, opts) 3 | % -> res: objective tracking 4 | % 5 | % solves min ||x-b|| with x constrained to a finite atomic norm ball (here 6 | % for the lasso, A is the set of scaled lasso columns and their negatives) 7 | % 8 | % This code runs away-steps FW by default. (Algorithm 1 in paper). 9 | % Set opts.pureFW to 1 to run standard FW. 10 | % 11 | % opts.Tmax % max number of iterations 12 | % opts.TOL % tolerance for convergence (FW gap stopping criterion) 13 | % opts.verbose % whether to print info 14 | % opts.pureFW % set to non-zero to use FW 15 | % if opts.pureFW is undefined or set to 0, then this runs away-steps FW 16 | 17 | [d,n] = size(A); % n is the number of atoms here (twice the number of lasso data columns) 18 | 19 | % init: 20 | % alpha_t will be a weight vector so that x_t = S_t * alpha_t 21 | alpha_t = zeros(n,1); alpha_t(1) = 1; 22 | x_t = A(:,1); % this is tracking A\alpha in the lasso case 23 | 24 | 25 | % each column of S_t = A(:,I_active) is a potential vertex 26 | % I_active -> contains index of active vertices (same as alpha_t > 0) 27 | % alpha_t(i) == 0 implies vertex is not active anymore... 28 | % alpha_t will be a weight vector so that x_t = A * alpha_t 29 | I_active = find(alpha_t > 0); 30 | 31 | % tracking results: 32 | 33 | fvalues = []; 34 | gap_values = []; 35 | number_away = 0; 36 | number_drop = 0; % counting drop steps (max stepsize for away step) 37 | 38 | pureFW = isfield(opts, 'pureFW') && opts.pureFW; 39 | if pureFW 40 | fprintf('running plain FW, for at most %d iterations\n', opts.Tmax); 41 | else % use away step 42 | fprintf('running FW with away steps, for at most %d iterations\n', opts.Tmax); 43 | end 44 | 45 | % optimization: 46 | it = 1; 47 | while it <= opts.Tmax 48 | it = it + 1; 49 | 50 | % cost function: 51 | f_t = objective_fun(x_t,b); 52 | % gradient = x-b: 53 | grad = grad_fun(x_t,b); 54 | 55 | % towards direction search: 56 | [id_FW, s_FW] = LMO(A,grad); % the linear minimization oracle, returning an atom 57 | 58 | d_FW = s_FW - x_t; 59 | 60 | % duality gap: 61 | gap = - d_FW' * grad; 62 | 63 | fvalues(it-1) = f_t; 64 | gap_values(it-1) = gap; 65 | 66 | if gap < opts.TOL 67 | fprintf('end of FW: reach small duality gap (gap=%g)\n', gap); 68 | break 69 | end 70 | 71 | % away direction search: 72 | [id_A, v_A] = away_step(grad, A, I_active); 73 | d_A = x_t - v_A; 74 | alpha_max = alpha_t(id_A); 75 | 76 | % construct direction (between towards and away): 77 | 78 | if pureFW || - gap <= d_A' * grad 79 | is_aw = false; 80 | d = d_FW; 81 | max_step = 1; 82 | else % away step 83 | is_aw = true; 84 | number_away = number_away+1; 85 | d = d_A; 86 | max_step = alpha_max / (1 - alpha_max); 87 | end 88 | 89 | % line search: 90 | %step = - (grad' * d) / ( d' * A * d ); % was this for the video QP 91 | step = - (grad' * d) / ( d' * d ); 92 | 93 | % simpler predefined stepsize 94 | %stepSize = 2 / (t+2); 95 | step = max(0, min(step, max_step )); 96 | 97 | 98 | if opts.verbose 99 | fprintf('it = %d - f = %g - gap=%g - stepsize=%g\n', it, f_t, gap, step); 100 | end 101 | 102 | if step < -eps 103 | % not a descent direction??? 104 | fprintf('ERROR -- not descent direction???') 105 | keyboard 106 | end 107 | 108 | % doing steps and updating active set: 109 | 110 | if is_aw 111 | %fprintf(' AWAY step from index %d\n',id_A); 112 | % away step: 113 | alpha_t = (1+step)*alpha_t; % note that inactive should stay at 0; 114 | if abs(step - max_step) < 10*eps 115 | % drop step: 116 | number_drop = number_drop+1; 117 | alpha_t(id_A) = 0; 118 | I_active(I_active == id_A) = []; % remove from active set 119 | else 120 | alpha_t(id_A) = alpha_t(id_A) - step; 121 | end 122 | else 123 | % FW step: 124 | alpha_t = (1-step)*alpha_t; 125 | 126 | alpha_t(id_FW) = alpha_t(id_FW) + step; 127 | 128 | I_active = find(alpha_t > 0); %TODO: could speed up by checking if id_FW is new 129 | 130 | % exceptional case: stepsize of 1, this collapses the active set! 131 | if step > 1-eps; 132 | I_active = [id_FW]; 133 | end 134 | end 135 | 136 | x_t = x_t + step * d; 137 | 138 | end 139 | 140 | function obj = objective_fun(x,b) 141 | obj = sum((x-b).^2)/2; 142 | %obj = .5 * norm(x - b, 2); %gives a different result 143 | end 144 | function grad = grad_fun(x,b) 145 | grad = x - b; 146 | end 147 | 148 | %LMO returns the index of, and the atom of max inner product with the negative gradient 149 | function [id, s] = LMO(A,grad) 150 | ips = grad' * A; 151 | [~,id] = min(ips); 152 | s = A(:,id); 153 | end 154 | 155 | % returns the id of the active atom with the worst value w.r.t. the 156 | % gradient 157 | function [id, v] = away_step(grad, A, I_active) 158 | ips = grad' * A(:,I_active); 159 | [~,id] = max(ips); 160 | id = I_active(id(1)); 161 | v = A(:,id); 162 | end 163 | 164 | 165 | res.primal = fvalues; 166 | res.gap = gap_values; 167 | res.number_away = number_away; 168 | res.number_drop = number_drop; 169 | res.S_t = A(:,I_active); 170 | res.alpha_t = alpha_t; 171 | res.x_t = x_t; 172 | 173 | end 174 | -------------------------------------------------------------------------------- /FW_video_colocalization/PFW.m: -------------------------------------------------------------------------------- 1 | function [x_t,f_t,res] = PFW(x_0, S_0, alpha_0, A, b, fun_optim, opts) 2 | % [x_t,f_t,res] = PFW(x_0, S_0, alpha_0, A, b, fun_optim, opts) 3 | % -> res: objective tracking 4 | % 5 | % This code runs pairwise FW (Algorithm 2 in paper). 6 | % 7 | % objective is: 0.5 x' A x + b' x 8 | % 9 | % [x_t, f_t] = PFW(x_0, S_0, alpha_0, A, fun_optim, opts) 10 | % x_0 : variable initialization 11 | % S_0 : active set initialization 12 | % alpha_0 : weights initialization 13 | % x_0 is a dx1 vector (d = dimension) 14 | % alpha_0 is k x 1 vector of convex combination weights 15 | % we need: x_0 = S_0 * alpha_0 16 | % S_0 is d x k, each column is an *atom* in the expansion for x_0 17 | % (it might also contains non-active atoms) 18 | % Simplest initialization: x_0, alpha_0 = [1] and S_0 = x_0 -> this is 19 | % valid *only if* x_0 is an atom (because the hashing function 20 | % expects specific structure for 21 | % atoms) 22 | % By using a more general hashing function, you could also handle 23 | % an arbitrary point as an atom... 24 | % 25 | % A, b : defines the cost function for QP 26 | % fun_optim : the linear minimization oracle function to solve the related LP: 27 | % min_{z in M} 28 | % for images : take the min of the integer solutions (eg z=(1,0,0) ) 29 | % for videos : shortest path algorithm 30 | % 31 | % opts.Tmax % max number of iterations 32 | % opts.TOL % tolerance for convergence (FW gap stopping criterion) 33 | % opts.verbose % whether to print progress 34 | % 35 | % res.primal = fvalues; 36 | % res.gap = gap_values; 37 | % res.number_away = number_away; 38 | % res.number_drop = number_drop; 39 | % res.S_t = S_t; 40 | % res.alpha_t = alpha_t; 41 | % res.x_t = x_t; 42 | % 43 | % IMPORTANT: to extend this code to a QP for another domain 44 | % -- you just need to provide a different linear oracle (fun_optim) 45 | % -- you also need to modify the hashing function below to the kind 46 | % of atoms appearing in your domain; it has to associate a unique 47 | % string (or number) to each possible atom which is output of your LMO. 48 | 49 | 50 | it = 1; 51 | minf = 0; 52 | minx = []; 53 | % init: 54 | 55 | x_t = x_0; 56 | S_t = S_0; 57 | alpha_t = alpha_0; 58 | 59 | % each column of S_t is a potential vertex 60 | % I_active -> contains index of active vertices (same as alpha_t > 0) 61 | mapping = containers.Map(); 62 | % this will map vertex hash to id in S_t (to see whether already seen vertex) 63 | % alpha_t(i) == 0 implies vertex is not active anymore... 64 | % alpha_t will be a weight vector so that x_t = S_t * alpha_t 65 | 66 | % constructing mapping: 67 | max_index = size(S_t,2); % keep track of size of S_t 68 | for index = 1:max_index 69 | mapping(hashing(S_t(:,index))) = index; 70 | end 71 | I_active = find(alpha_t > 0); 72 | 73 | % tracking results: 74 | 75 | fvalues = []; 76 | gap_values = []; 77 | number_drop = 0; % counting drop steps (max stepsize for away step) 78 | 79 | fprintf('running pairwise FW, for at most %d iterations\n', opts.Tmax); 80 | 81 | cost_fun =@(y) .5 * y' *A * y + b' * y; 82 | % optimization: 83 | 84 | while it <= opts.Tmax 85 | it = it + 1; 86 | 87 | % gradient: 88 | grad = A * x_t + b; 89 | 90 | % cost function: 91 | f_t = cost_fun(x_t); 92 | 93 | % towards direction search: 94 | s_FW = fun_optim( grad ); % the linear minimization oracle 95 | d_FW = s_FW - x_t; 96 | 97 | % duality gap: 98 | gap = - d_FW' * grad; 99 | 100 | fvalues(it-1) = f_t; 101 | gap_values(it-1) = gap; 102 | 103 | if opts.verbose 104 | fprintf('it = %d - f = %g - gap=%g\n', it, f_t, gap); 105 | end 106 | 107 | if gap < opts.TOL 108 | fprintf('end of PFW: reach small duality gap (gap=%g)\n', gap); 109 | break 110 | end 111 | 112 | % away direction search: 113 | 114 | if ~isempty(S_t) 115 | id_A = away_step(grad, S_t, I_active); 116 | v_A = S_t(:, id_A); 117 | %d_A = x_t - v_A; 118 | alpha_max = alpha_t(id_A); 119 | else 120 | fprintf('error: empty support set at step (it=%f)\n', it); 121 | end 122 | 123 | % construct pair direction (between towards and away): 124 | 125 | d = s_FW - v_A; %was for away: d_A; 126 | max_step = alpha_max; % was for away: alpha_max / (1 - alpha_max); 127 | 128 | % line search: (same as away, but different d) 129 | 130 | step = - (grad' * d) / ( d' * A * d ); 131 | step = max(0, min(step, max_step )); 132 | 133 | if step < -eps 134 | % not a descent direction??? 135 | fprintf('ERROR -- not descent direction???') 136 | keyboard 137 | end 138 | 139 | % doing steps and updating active set: 140 | 141 | % away part of the step step: 142 | %alpha_t = (1+step)*alpha_t; % note that inactive should stay at 0; 143 | if abs(step - max_step) < 10*eps 144 | % drop step: 145 | number_drop = number_drop+1; 146 | alpha_t(id_A) = 0; 147 | I_active(I_active == id_A) = []; % remove from active set 148 | %TODO: could possibly also remove it from S_t 149 | else 150 | alpha_t(id_A) = alpha_t(id_A) - step; 151 | end 152 | 153 | % towards part of the step: 154 | % alpha_t = (1-step)*alpha_t; 155 | 156 | % is this s_FW a new vertex? 157 | h = hashing(s_FW); 158 | if ~mapping.isKey(h) 159 | % we need to add vertex in S_t: 160 | max_index = max_index + 1; 161 | mapping(h) = max_index; 162 | S_t(:,max_index) = s_FW; 163 | id_FW = max_index; 164 | alpha_t(id_FW) = step; % this increase size of alpha_t btw 165 | I_active = [I_active, id_FW]; 166 | else 167 | id_FW = mapping(h); 168 | if alpha_t(id_FW) < eps 169 | % we already had atom in 'correction poytope', but it was not 170 | % active, so now track it as active: 171 | I_active = [I_active, id_FW]; 172 | end 173 | alpha_t(id_FW) = alpha_t(id_FW) + step; 174 | end 175 | 176 | % exceptional case: stepsize of 1, this collapses the active set! 177 | if step > 1-eps; 178 | I_active = [id_FW]; 179 | end 180 | 181 | x_t = x_t + step * d; 182 | 183 | end 184 | 185 | 186 | % returns the id of the active atom with the worst value w.r.t. the 187 | % gradient 188 | function id = away_step(grad, S, I_active) 189 | s = grad' * S(:,I_active); 190 | [~,id] = max(s); 191 | id = I_active(id(1)); 192 | end 193 | 194 | % MODIFY THE FOLLOWING FUNCTION for the atoms in your domain 195 | function h = hashing(sequence) 196 | % sequence is 0-1 vector 197 | % output is string with a for 0, b for 1. 198 | h = repmat('a',1,length(sequence)); 199 | h(sequence == 1) = 'b'; 200 | end 201 | 202 | 203 | res.primal = fvalues; 204 | res.gap = gap_values; 205 | res.number_drop = number_drop; 206 | res.S_t = S_t; 207 | res.alpha_t = alpha_t; 208 | res.x_t = x_t; 209 | 210 | end 211 | -------------------------------------------------------------------------------- /FW_video_colocalization/AFW.m: -------------------------------------------------------------------------------- 1 | function [x_t,f_t,res] = AFW(x_0, S_0, alpha_0, A, b, fun_optim, opts) 2 | % [x_t,f_t,res] = AFW(x_0, S_0, alpha_0, A, b, fun_optim, opts) 3 | % -> res: objective tracking 4 | % 5 | % This code runs away-steps FW (Algorithm 1 in paper). 6 | % 7 | % objective is: 0.5 x' A x + b' x 8 | % 9 | % [x_t, f_t] = AFW(x_0, S_0, alpha_0, A, fun_optim, opts) 10 | % x_0 : variable initialization 11 | % S_0 : active set initialization 12 | % alpha_0 : weights initialization 13 | % x_0 is a dx1 vector (d = dimension) 14 | % alpha_0 is k x 1 vector of convex combination weights 15 | % we need: x_0 = S_0 * alpha_0 16 | % S_0 is d x k, each column is an *atom* in the expansion for x_0 17 | % (it might also contains non-active atoms) 18 | % Simplest initialization: x_0, alpha_0 = [1] and S_0 = x_0 -> this is 19 | % valid *only if* x_0 is an atom (because the hashing function 20 | % expects specific structure for 21 | % atoms) 22 | % By using a more general hashing function, you could also handle 23 | % an arbitrary point as an atom... 24 | % 25 | % A, b : defines the cost function for QP 26 | % fun_optim : the linear minimization oracle function to solve the related LP: 27 | % min_{z in M} 28 | % for images : take the min of the integer solutions (eg z=(1,0,0) ) 29 | % for videos : shortest path algorithm 30 | % 31 | % opts.Tmax % max number of iterations 32 | % opts.TOL % tolerance for convergence (FW gap stopping criterion) 33 | % opts.verbose % whether to print progress 34 | % 35 | % res.primal = fvalues; 36 | % res.gap = gap_values; 37 | % res.number_away = number_away; 38 | % res.number_drop = number_drop; 39 | % res.S_t = S_t; 40 | % res.alpha_t = alpha_t; 41 | % res.x_t = x_t; 42 | % 43 | % IMPORTANT: to extend this code to a QP for another domain 44 | % -- you just need to provide a different linear oracle (fun_optim) 45 | % -- you also need to modify the hashing function below to the kind 46 | % of atoms appearing in your domain; it has to associate a unique 47 | % string (or number) to each possible atom which is output of your LMO. 48 | 49 | it = 1; 50 | minf = 0; 51 | minx = []; 52 | % init: 53 | 54 | x_t = x_0; 55 | S_t = S_0; 56 | alpha_t = alpha_0; 57 | 58 | % each column of S_t is a potential vertex 59 | % I_active -> contains index of active vertices (same as alpha_t > 0) 60 | mapping = containers.Map(); 61 | % this will map vertex hash to id in S_t (to see whether already seen vertex) 62 | % alpha_t(i) == 0 implies vertex is not active anymore... 63 | % alpha_t will be a weight vector so that x_t = S_t * alpha_t 64 | 65 | % constructing mapping: 66 | max_index = size(S_t,2); % keep track of size of S_t 67 | for index = 1:max_index 68 | mapping(hashing(S_t(:,index))) = index; 69 | end 70 | I_active = find(alpha_t > 0); 71 | 72 | % tracking results: 73 | 74 | fvalues = []; 75 | gap_values = []; 76 | number_away = 0; 77 | number_drop = 0; % counting drop steps (max stepsize for away step) 78 | 79 | 80 | fprintf('running away-steps FW, for at most %d iterations\n', opts.Tmax); 81 | 82 | cost_fun =@(y) .5 * y' *A * y + b' * y; 83 | % optimization: 84 | 85 | while it <= opts.Tmax 86 | it = it + 1; 87 | 88 | % gradient: 89 | grad = A * x_t + b; 90 | 91 | % cost function: 92 | f_t = cost_fun(x_t); 93 | 94 | % towards direction search: 95 | s_FW = fun_optim( grad ); % the linear minimization oracle 96 | d_FW = s_FW - x_t; 97 | 98 | % duality gap: 99 | gap = - d_FW' * grad; 100 | 101 | fvalues(it-1) = f_t; 102 | gap_values(it-1) = gap; 103 | 104 | if opts.verbose 105 | fprintf('it = %d - f = %g - gap=%g\n', it, f_t, gap); 106 | end 107 | 108 | if gap < opts.TOL 109 | fprintf('end of AFW: reach small duality gap (gap=%g)\n', gap); 110 | break 111 | end 112 | 113 | % away direction search: 114 | 115 | if ~isempty(S_t) 116 | id_A = away_step(grad, S_t, I_active); 117 | v_A = S_t(:, id_A); 118 | d_A = x_t - v_A; 119 | alpha_max = alpha_t(id_A); 120 | else 121 | fprintf('error: empty support set at step (it=%f)\n', it); 122 | end 123 | 124 | % construct direction (between towards and away): 125 | 126 | if isempty(S_t) || - gap <= d_A' * grad 127 | is_aw = false; % this is a pure FW step 128 | d = d_FW; 129 | max_step = 1; 130 | else 131 | is_aw = true; % this is an away step 132 | number_away = number_away+1; 133 | d = d_A; 134 | max_step = alpha_max / (1 - alpha_max); 135 | end 136 | 137 | % line search: 138 | 139 | step = - (grad' * d) / ( d' * A * d ); 140 | step = max(0, min(step, max_step )); 141 | 142 | if step < -eps 143 | % not a descent direction??? 144 | fprintf('ERROR -- not descent direction???') 145 | keyboard 146 | end 147 | 148 | % doing steps and updating active set: 149 | 150 | if is_aw 151 | % away step: 152 | alpha_t = (1+step)*alpha_t; % note that inactive should stay at 0; 153 | if abs(step - max_step) < 10*eps 154 | % drop step: 155 | number_drop = number_drop+1; 156 | alpha_t(id_A) = 0; 157 | I_active(I_active == id_A) = []; % remove from active set 158 | %TODO: could possibly also remove it from S_t 159 | else 160 | alpha_t(id_A) = alpha_t(id_A) - step; 161 | end 162 | else 163 | % FW step: 164 | alpha_t = (1-step)*alpha_t; 165 | 166 | % is this s_FW a new vertex? 167 | h = hashing(s_FW); 168 | if ~mapping.isKey(h) 169 | % we need to add vertex in S_t: 170 | max_index = max_index + 1; 171 | mapping(h) = max_index; 172 | S_t(:,max_index) = s_FW; 173 | id_FW = max_index; 174 | alpha_t(id_FW) = step; % this increase size of alpha_t btw 175 | I_active = [I_active, id_FW]; 176 | else 177 | id_FW = mapping(h); 178 | if alpha_t(id_FW) < eps 179 | % we already had atom in 'correction poytope', but it was not 180 | % active, so now track it as active: 181 | I_active = [I_active, id_FW]; 182 | end 183 | alpha_t(id_FW) = alpha_t(id_FW) + step; 184 | end 185 | 186 | % exceptional case: stepsize of 1, this collapses the active set! 187 | if step > 1-eps; 188 | I_active = [id_FW]; 189 | end 190 | end 191 | 192 | x_t = x_t + step * d; 193 | 194 | end 195 | 196 | 197 | % returns the id of the active atom with the worst value w.r.t. the 198 | % gradient 199 | function id = away_step(grad, S, I_active) 200 | s = grad' * S(:,I_active); 201 | [~,id] = max(s); 202 | id = I_active(id(1)); 203 | end 204 | 205 | % MODIFY THE FOLLOWING FUNCTION for the atoms in your domain 206 | function h = hashing(sequence) 207 | % sequence is 0-1 vector 208 | % output is string with a for 0, b for 1. 209 | h = repmat('a',1,length(sequence)); 210 | h(sequence == 1) = 'b'; 211 | end 212 | 213 | 214 | res.primal = fvalues; 215 | res.gap = gap_values; 216 | res.number_away = number_away; 217 | res.number_drop = number_drop; 218 | res.S_t = S_t; 219 | res.alpha_t = alpha_t; 220 | res.x_t = x_t; 221 | 222 | end 223 | -------------------------------------------------------------------------------- /FW_video_colocalization/solvers/mexUtil.h: -------------------------------------------------------------------------------- 1 | #ifndef OLI_UTILS 2 | #define OLI_UTILS 1 3 | 4 | 5 | #include "mex.h" 6 | #include 7 | using namespace std; 8 | 9 | 10 | 11 | 12 | void myCheckArgNumber(int nrhs, int nIn = -1, int nlhs = NULL , int nOut = -1) 13 | { 14 | if(nIn != -1) 15 | if (nrhs != nIn) 16 | { 17 | stringstream errMsg; 18 | errMsg << nIn << " input arguments required." << endl; 19 | mexErrMsgTxt(errMsg.str().c_str()); 20 | } 21 | if(nOut != -1) 22 | if (nlhs != nOut) 23 | { 24 | stringstream errMsg; 25 | errMsg << nOut << " output arguments required." << endl; 26 | mexErrMsgTxt(errMsg.str().c_str()); 27 | } 28 | } 29 | 30 | enum{ myDouble , myInt , mySingle }; 31 | 32 | double* myCheckArg(const mxArray *prhs[],int indArg,int m = -1 ,int n = -1, int type = -1) 33 | { 34 | if(m!=-1) 35 | { 36 | if(mxGetM(prhs[indArg])!=m) 37 | { 38 | stringstream errMsg; 39 | errMsg << "argument number " << indArg+1 << " should have " << m << " rows." << endl; 40 | mexErrMsgTxt(errMsg.str().c_str()); 41 | } 42 | } 43 | if(n!=-1) 44 | { 45 | if(mxGetN(prhs[indArg])!=n) 46 | { 47 | stringstream errMsg; 48 | errMsg << "argument number " << indArg+1 << " should have " << n << " columns." << endl; 49 | mexErrMsgTxt(errMsg.str().c_str()); 50 | } 51 | } 52 | switch(type) 53 | { 54 | case myDouble: 55 | if(!mxIsDouble(prhs[indArg])) 56 | { 57 | stringstream errMsg; 58 | errMsg << "argument number " << indArg+1 << " should be double." << endl; 59 | mexErrMsgTxt(errMsg.str().c_str()); 60 | } 61 | break; 62 | case myInt: 63 | if(!mxIsInt32(prhs[indArg])) 64 | { 65 | stringstream errMsg; 66 | errMsg << "argument number " << indArg+1 << " should be integer." << endl; 67 | mexErrMsgTxt(errMsg.str().c_str()); 68 | } 69 | break; 70 | case mySingle: 71 | if(!mxIsSingle(prhs[indArg])) 72 | { 73 | stringstream errMsg; 74 | errMsg << "argument number " << indArg+1 << " should be single." << endl; 75 | mexErrMsgTxt(errMsg.str().c_str()); 76 | } 77 | break; 78 | } 79 | // if(mxIsDouble(prhs[indArg])) 80 | // return mxGetPr(prhs[indArg]); 81 | return mxGetPr(prhs[indArg]); 82 | } 83 | 84 | double* myCheckArg(const mxArray *prhs[],int indArg, int* mOut ,int n = -1, int type = -1) 85 | { 86 | *mOut = mxGetM(prhs[indArg]); 87 | return myCheckArg(prhs,indArg,-1,n,type); 88 | } 89 | double* myCheckArg(const mxArray *prhs[],int indArg, int m ,int* nOut, int type = -1) 90 | { 91 | *nOut = mxGetN(prhs[indArg]); 92 | return myCheckArg(prhs,indArg,m,-1,type); 93 | } 94 | double* myCheckArg(const mxArray *prhs[],int indArg, int* mOut ,int* nOut, int type = -1) 95 | { 96 | *mOut = mxGetM(prhs[indArg]); 97 | *nOut = mxGetN(prhs[indArg]); 98 | return myCheckArg(prhs,indArg,-1,-1,type); 99 | } 100 | 101 | double* myCheckArg3(const mxArray *prhs[],int indArg, int* mOut ,int* nOut, int* pOut, int type = -1) 102 | { 103 | if(mxGetNumberOfDimensions(prhs[indArg])!=3) 104 | { 105 | stringstream errMsg; 106 | errMsg << "argument number " << indArg+1 << " should have 3 dimensions." << endl; 107 | mexErrMsgTxt(errMsg.str().c_str()); 108 | } 109 | const int* size =(const int*) mxGetDimensions(prhs[indArg]); 110 | *mOut = size[0]; 111 | *nOut = size[1]; 112 | *pOut = size[2]; 113 | switch(type) 114 | { 115 | case 0: 116 | if(!mxIsDouble(prhs[indArg])) 117 | { 118 | stringstream errMsg; 119 | errMsg << "argument number " << indArg+1 << " should be double." << endl; 120 | mexErrMsgTxt(errMsg.str().c_str()); 121 | } 122 | break; 123 | case 1: 124 | if(!mxIsInt32(prhs[indArg])) 125 | { 126 | stringstream errMsg; 127 | errMsg << "argument number " << indArg+1 << " should be integer." << endl; 128 | mexErrMsgTxt(errMsg.str().c_str()); 129 | } 130 | break; 131 | } 132 | return mxGetPr(prhs[indArg]); 133 | } 134 | 135 | double* myCheckArg4(const mxArray *prhs[],int indArg, int* mOut ,int* nOut, int* pOut, int* qOut , int type = -1) 136 | { 137 | if(mxGetNumberOfDimensions(prhs[indArg])!=4) 138 | { 139 | stringstream errMsg; 140 | errMsg << "argument number " << indArg+1 << " should have 4 dimensions." << endl; 141 | mexErrMsgTxt(errMsg.str().c_str()); 142 | } 143 | const int* size = (const int*) mxGetDimensions(prhs[indArg]); 144 | *mOut = size[0]; 145 | *nOut = size[1]; 146 | *pOut = size[2]; 147 | *qOut = size[3]; 148 | switch(type) 149 | { 150 | case 0: 151 | if(!mxIsDouble(prhs[indArg])) 152 | { 153 | stringstream errMsg; 154 | errMsg << "argument number " << indArg+1 << " should be double." << endl; 155 | mexErrMsgTxt(errMsg.str().c_str()); 156 | } 157 | break; 158 | case 1: 159 | if(!mxIsInt32(prhs[indArg])) 160 | { 161 | stringstream errMsg; 162 | errMsg << "argument number " << indArg+1 << " should be integer." << endl; 163 | mexErrMsgTxt(errMsg.str().c_str()); 164 | } 165 | break; 166 | } 167 | return mxGetPr(prhs[indArg]); 168 | } 169 | 170 | double* myCheckArg5(const mxArray *prhs[],int indArg, int* mOut ,int* nOut, int* pOut, int* qOut , int* rOut, int type = -1) 171 | { 172 | if(mxGetNumberOfDimensions(prhs[indArg]) != 5) 173 | { 174 | stringstream errMsg; 175 | errMsg << "argument number " << indArg+1 << " should have 5 dimensions." << endl; 176 | mexErrMsgTxt(errMsg.str().c_str()); 177 | } 178 | const int* size = (const int*) mxGetDimensions(prhs[indArg]); 179 | *mOut = size[0]; 180 | *nOut = size[1]; 181 | *pOut = size[2]; 182 | *qOut = size[3]; 183 | *rOut = size[4]; 184 | switch(type) 185 | { 186 | case 0: 187 | if(!mxIsDouble(prhs[indArg])) 188 | { 189 | stringstream errMsg; 190 | errMsg << "argument number " << indArg+1 << " should be double." << endl; 191 | mexErrMsgTxt(errMsg.str().c_str()); 192 | } 193 | break; 194 | case 1: 195 | if(!mxIsInt32(prhs[indArg])) 196 | { 197 | stringstream errMsg; 198 | errMsg << "argument number " << indArg+1 << " should be integer." << endl; 199 | mexErrMsgTxt(errMsg.str().c_str()); 200 | } 201 | break; 202 | } 203 | return mxGetPr(prhs[indArg]); 204 | } 205 | 206 | double* myCheckArg6(const mxArray *prhs[],int indArg, int* mOut ,int* nOut, int* pOut, int* qOut , int* rOut, int* sOut, int type = -1) 207 | { 208 | if(mxGetNumberOfDimensions(prhs[indArg]) != 6) 209 | { 210 | stringstream errMsg; 211 | errMsg << "argument number " << indArg+1 << " should have 6 dimensions." << endl; 212 | mexErrMsgTxt(errMsg.str().c_str()); 213 | } 214 | const int* size = (const int*) mxGetDimensions(prhs[indArg]); 215 | *mOut = size[0]; 216 | *nOut = size[1]; 217 | *pOut = size[2]; 218 | *qOut = size[3]; 219 | *rOut = size[4]; 220 | *sOut = size[5]; 221 | switch(type) 222 | { 223 | case 0: 224 | if(!mxIsDouble(prhs[indArg])) 225 | { 226 | stringstream errMsg; 227 | errMsg << "argument number " << indArg+1 << " should be double." << endl; 228 | mexErrMsgTxt(errMsg.str().c_str()); 229 | } 230 | break; 231 | case 1: 232 | if(!mxIsInt32(prhs[indArg])) 233 | { 234 | stringstream errMsg; 235 | errMsg << "argument number " << indArg+1 << " should be integer." << endl; 236 | mexErrMsgTxt(errMsg.str().c_str()); 237 | } 238 | break; 239 | } 240 | return mxGetPr(prhs[indArg]); 241 | } 242 | #endif 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | --------------------------------------------------------------------------------