Как исправить Допустимые индексы для «переменной» ограничены ошибкой циклов PARFOR в Matlab

Я пытаюсь настроить вложенный цикл parfor в MatLab R2016a, как показано ниже.

N = size(A,1);
M = size(v,1);
in = zeros(N*M,1);
parfor i=1:N
   for j=1:M
      k = (i-1)*M+j;
      if sqrt(sum((A(i,:)-v(j,:)).^2))<=tol
        in(k) = i;          
      end
   end
end

Однако я получаю следующую ошибку: Действительные индексы для 'in' ограничены в циклах PARFOR. Можно ли как-то исправить это, поскольку оба массива A и v значительно велики, более 40 000 строк для A и 8 000 v? Переменная tol равна 0,0959.


person Yixi Shan    schedule 08.06.2018    source источник
comment
Обратите внимание, что вы выполняете множество нормальных вычислений столбцов матрицы, которые вы можете сделать сразу, используя vecnorm, если ваш Matlab 2017b и выше.   -  person percusse    schedule 08.06.2018
comment
Версия Matlab — R2016a. Как указано в первой строке вопроса.   -  person Yixi Shan    schedule 08.06.2018
comment
Тут ошибка от того, что вы распараллеливаете in но параллельные задачи пытаются писать в одно и то же   -  person percusse    schedule 08.06.2018
comment
@percusse Можете ли вы уточнить ответ ниже? @SardarUsama Нет, значение k никогда не будет иметь одинаковое значение для нескольких итераций i, поскольку оно также зависит от j. k считает от 1:M*N.   -  person Yixi Shan    schedule 08.06.2018
comment
Это опечатка в исходном вопросе, это k=(i-1)*M+j   -  person Yixi Shan    schedule 11.06.2018


Ответы (2)


Проблема в том, что MATLAB не распознает, что переменная k правильно разрезает матрицу in. Решение должно состоять в том, чтобы индексировать in, используя i и j отдельно:

N = size(A,1);
M = size(v,1);
in = zeros(M,N);
parfor i=1:N
   for j=1:M
      if sqrt(sum((A(i,:)-v(j,:)).^2))<=tol
        in(j,i) = i;          
      end
   end
end
in = in(:); % reshape to a column vector, as the output in the question's code

Другая альтернатива, но она требует больше промежуточной памяти, состоит в том, чтобы вычислить это вообще без цикла:

A = reshape(A,1,N,[]);
v = reshape(v,M,1,[]);
in = sum(bsxfun(@minus,A,v).^2,3) < tol*tol;
in = in(:);

(Или что-то подобное, я не запускал этот код... Пожалуйста, дайте мне знать или исправьте пост, если есть опечатка или другая ошибка.)

person Cris Luengo    schedule 11.06.2018
comment
Первый метод в вашем ответе работает отлично, спасибо. Я мог бы попробовать второй позже, если время позволит. - person Yixi Shan; 11.06.2018
comment
Единственное, что я добавил, это s=v; сразу после parfor, как это было сделано в другом ответе. Я не знаю, было ли это необходимо, но MatLab предупредил. - person Yixi Shan; 11.06.2018

N = size(A,1);
M = size(v,1);
in = cell(N,1);
parfor i=1:N
    s=v;
    p=zeros(1:M,1);
   for j=1:M
      k = (i-1)*M+j;
      if sqrt(sum((A(i,:)-s(j,:)).^2))<=tol
        p(k) = i;          
      end
   end
   in{i}=single(p);
end
in=cell2mat(in);
in=reshape(in,[N*M,1]);

иногда Matlab не распознает переменную в цикле parfor как нарезанную переменную, нарезанная переменная — это переменная, которая имеет ссылку вне цикла parfor, и каждый из ее элементов доступен только одному рабочему (в parfor параллельных рабочих)
поэтому вы можно использовать временную переменную и собирать результаты после цикла parfor,

ПРИМЕЧАНИЕ 1. В более старых версиях код лучше векторизовать, потому что циклы не были такими хорошими, как сейчас в R2017, ссылаясь на (это).

ПРИМЕЧАНИЕ 2: если большинство элементов in равно нулю, попробуйте использовать разреженную матрицу, которая может сэкономить много памяти;

person Hadi    schedule 10.06.2018
comment
Это может быть что-то, что я изучаю на компьютере, который я использую, но я получаю сообщение об ошибке нехватки памяти. В противном случае, похоже, он работает. Есть ли способ сохранить индексы in как несколько переменных, а затем рекомбинировать их вне цикла parfor или что-то еще, чтобы попытаться решить эту проблему с памятью? - person Yixi Shan; 11.06.2018
comment
Можно ли добавить что-то вроде это в решение ? - person Yixi Shan; 11.06.2018
comment
@YixiShan, я обновил ответ в соответствии с вашим комментарием, он потребляет гораздо меньше памяти, чем предыдущий код (код можно без проблем запускать в памяти 8G) - person Hadi; 11.06.2018
comment
Спасибо, к сожалению, на моей машине доступно только 4 Гб памяти. Я думаю, что лучшее решение - изменить ваш ответ, чтобы он был только с интервалом в четверть от 1: N или что-то в этом роде, а затем объединить массивы вместе в конце. Также я знаю, что будет храниться много повторяющихся значений, которые я планировал удалить, используя unique в конце parfor. Можно ли реализовать это раньше, чтобы сократить размер in? Если нет, то, возможно, запуск parfor с интервалом в четверть или меньше может исправить это. - person Yixi Shan; 11.06.2018
comment
@YixiShan имеет 40000 * 8000 = 32e7 элементов, каждый из которых имеет 4 байта, поэтому его размер составляет 1,1921 ГБ, вам нужна дополнительная свободная память (размером около in) для некоторых операций, поэтому вы можете сделать некоторые parfor, сохранить данные на жесткий диск , очистить переменные и продолжить parfor и т. д.; - person Hadi; 11.06.2018