Неполное разложение Холесского разреженных матриц

Алгоритмы: от сортировки пузырьком до численных методов

Модераторы: C_O_D_E, DeeJayC

Ответить
vitaly333
Сообщения: 4
Зарегистрирован: 05 апр 2007, 19:07

В данный момент занимаюсь написанием предобуславливателя к итерационоому решателю Conjuate Gradiuent (метод сопряженных градиентов) для sparse - матриц. Матрица храниться в 3-х одномерных массивах (разреженный строчный формат для симметричных матриц CSIR).
Пример:

Код: Выделить всё


A = 
9.0   0.0   0.0   3.0   1.0   0.0   1.0    
0.0   11.0   2.0   1.0   0.0   0.0   2.0    
0.0   2.0   10.0   2.0   0.0   0.0   0.0    
3.0   1.0   2.0   9.0   1.0   0.0   0.0    
1.0   0.0   0.0   1.0   12.0   0.0   1.0    
0.0   0.0   0.0   0.0   0.0   8.0   0.0    
1.0   2.0   0.0   0.0   1.0   0.0   8.0    

------------- Разреженная матрица ------------ 
Формат хранения: CSIR  
AN: 9.0 3.0 1.0 1.0 11.0 2.0 1.0 2.0 10.0 2.0 9.0 1.0 12.0 1.0 8.0 8.0 
JA: 0.0 3.0 4.0 6.0 1.0 2.0 3.0 6.0 2.0 3.0 3.0 4.0 4.0 6.0 5.0 6.0 
IA: 0.0 4.0 8.0 10.0 12.0 14.0 15.0 16.0 
----------------------------------------------- 

В качестве улутшателя взял Неполное разложение Холецкого (Incomplete Cholesky). Привожу вам мой алгоритм:

Код: Выделить всё


/**
	 * Неполная факторизация Холецкого разреженной матрицы
	 * @param  AN - массив, содержащий все ненулевые над диагональные элементы матрицы A, включая и диагональные. 
	 * Каждая новая строка этого массива начинается с диагонального элемента.
	 * @param  JA - одномерный массив, который содержит столько же элементов, сколько и
     * массив  AN и для каждого из них указывает, в каком столбце находиться данный
     * элемент. 
	 * @param  IA - одномерный массив, элементы которого указывают на позиции, с
     * которых начинается описание очередной строки.
	 * @return u - массив, содержащий ненулевые наддиагональные елементы, получившиеся после неполной факторизации Холецкого  
	 */
public static double[] IC_Decomposition(double[] AN,int[] JA,int[] IA,double[] u ){
int i,j,k;
    double sum2=0, m=0,r=0;
    int nz =AN.length;//количество ненулевых элементов, не считая диагональные  
    int n = IA.length-1;//количество строк (столбцов), матрицы	
    double[] sum = new double[n];
    
u[0]=Math.sqrt(AN[0]);
       
  //находим элементы 0 - ой  строки 
for (i=IA[0];i<IA[1];i++) {
   u[i]=AN[i]/u[0]; 
   sum[JA[i]]=u[i]*u[i];//запоминаем квадраты получившихся элементов			
}
			
	//Ищем остальные факторизованные элементы , начиная с 1-ой строки		
for (i=1;i<n;i++){
			
u[IA[i]]=Math.sqrt(AN[IA[i]]-sum[i]);//находим диагональные элементы	
			
			      
for (j=IA[i]+1;j<IA[i+1];j++){
			
u[j] = AN[j]; 			
			 
//Поиск cуммы элементов a[k][i] и a[k][j]. 
/////////////////////////////////////////////////////////////////				 
		
for ( k = 0; k < i; k++) 
	    { 
 int n1 = -1; 
	 for (int ii = IA[k]+1; ii < IA[k + 1]; ii++) 
	            { 
	               if (JA[ii] == i) n1 = ii; 
	               if (JA[ii] == JA[j]) 
	               { 
	                  if (n1 >= 0) u[j] -= u[n1]*u[ii]; 
	                  break; 
	               } 
	            } 
	         } 
u[j]/=u[IA[i]];//Находим остальные над диагональные элементы


			 
				
sum[JA[j]]+=u[j]*u[j];
				
			}
			
		}
return u;	
	}
	

Также я нашел в инэте ещё один алгоритм Неполного разложения Холесского , он используется во всех открытых библиотеках с итерационными решателями(ITL , IML++, ISTL и др.)

Вот его код:

Код: Выделить всё


static void 
ICFactor(const MV_Vector<int> &pntr, const MV_Vector<int> &indx, 
     MV_Vector<double> &val) 
{ 
  int d, g, h, i, j, k, n = pntr.size() - 1; 
  double z; 

  for (k = 0; k < n - 1; k++) { 
    d = pntr[k]; 
    z = val[d] = sqrt(val[d]); 

    for (i = d + 1; i < pntr[k+1]; i++) 
      val[i] /= z; 

    for (i = d + 1; i < pntr[k+1]; i++) { 
      z = val[i]; 
      h = indx[i]; 
      g = i; 

      for (j = pntr[h] ; j < pntr[h+1]; j++) 
    for ( ; g < pntr[k+1] && indx[g+1] <= indx[j]; g++) 
      if (indx[g] == indx[j]) 
        val[j] -= z * val[g]; 
    } 
  } 
  d = pntr[n-1]; 
  val[d] = sqrt(val[d]); 
} 
vitaly333
Сообщения: 4
Зарегистрирован: 05 апр 2007, 19:07

И я решил переписать его под свою программу, принимая во
внимание то что здесь val = аналог моего AN , pntr = IA и indx = JA
Вот переписанный мною код:

Код: Выделить всё

public static  double[]  IC_Decomposition2(double[] AN,int[] JA,int[] IA ){ 
        int d, g, h, i, j, k; 
        double z; 
        int nz = AN.length; 
        int n = IA.length-1; 
        
        for (k = 0; k < n - 1; k++) { 
          d =(int) IA[k]; 
          z = AN[d] = Math.sqrt(AN[d]); 
          
          for (i = d + 1; i < IA[k+1]; i++) 
            AN[i] /= z; 
          
        
          for (i = d + 1; i < IA[k+1]; i++) { 
            z = AN[i]; 
            h = JA[i]; 
            g = i; 
            
            for (j =IA[h] ; j < IA[h+1]; j++) 
              for ( ; g < IA[k+1] && JA[g+1] <= JA[j]; g++) 
                if (JA[g] == JA[j]) 
                  AN[j] -= z * AN[g]; 
          
          } 
        
        } 
        
          
        d = IA[n-1]; 
        AN[d] = Math.sqrt(AN[d]); 

   return AN;    
   } 

Я решил теперь сравнить IC_Decomposition и IC_Decomposition2 и разложил ту матрицу 7x7, которую я приводил выше, вот что получилось:
1. По моему алгоритму IC_Decomposition:

Код: Выделить всё


3.0 
1.0 
0.3333333333333333 
0.3333333333333333 
3.3166247903554 
0.6030226891555273 
0.30151134457776363 
0.6030226891555273 
3.1042492870843406 
0.5857074126574228 
2.750643149492325 
0.24236755930688816 
3.439498052781032 
0.2584356424246762 
2.8284271247461903 
2.731018774006702      
   
2. По алгоритму IC_Decomposition2:

Код: Выделить всё

3.0 
1.0 
0.3333333333333333 
0.3333333333333333 
3.3166247903554 
0.6030226891555273 
0.30151134457776363 
0.6030226891555273 
3.1622776601683795 
0.6324555320336759 
2.932575659723036 
0.34099716973523675 
3.4472773213410837 
0.2578524458667825 
2.8284271247461903 
2.7310738989293286 

Как вы видите решение по IC_Decomposition2, отличается от IC_Decomposition(моего). Попробовал в Matlab разложить эту матрицу решение получилось точно такое же как и IC_Decomposition,т.е получается IC_Decomposition2 дает решение приближенное к точному неполному разложению Холесского.
vitaly333
Сообщения: 4
Зарегистрирован: 05 апр 2007, 19:07

Теперь я решил оценить быстродействие двух алгоритмов:
и разложил матрицу 30x30 100000 раз:
Вот матрица:

Код: Выделить всё

46 -11 0 -21 8 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
-11 6 -2 8 -4 -3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 -2 10 -9 3 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
-21 8 -9 37 -19 -1 -16 11 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
8 -4 3 -19 14 -2 11 -10 -5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
9 -3 2 -1 -2 10 -7 5 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 -16 11 -7 26 -22 -1 -10 11 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 11 -10 5 -22 26 -1 11 -16 -7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 7 -5 2 -1 -1 10 -5 7 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 -10 11 -5 14 -19 -2 -4 8 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 11 -16 7 -19 37 -1 8 -21 -9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 5 -7 2 -2 -1 10 -3 9 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 -4 8 -3 6 -11 -2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 8 -21 9 -11 46 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 3 -9 2 -2 0 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 43 -106 22 -36 78 33 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -106 413 5 78 -192 -81 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 22 5 91 -33 81 22 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -36 78 -33 121 -184 19 -85 106 53 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 78 -192 81 -184 335 11 106 -142 -69 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 33 -81 22 19 11 91 -53 69 22 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -85 106 -53 228 -213 16 -142 106 69 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 106 -142 69 -213 228 16 106 -85 -53 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 53 -69 22 16 16 91 -69 53 22 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -142 106 -69 335 -184 11 -192 78 81 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 106 -85 53 -184 121 19 78 -36 -33 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 69 -53 22 11 19 91 -81 33 22 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -192 78 -81 413 -106 5 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 78 -36 33 -106 43 22 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 81 -33 22 5 22 91 
Вот она в упакованном виде:
------------- Разреженная матрица ------------ 
Формат хранения: CSIR 
AN: 46.0 -11.0 -21.0 8.0 9.0 6.0 -2.0 8.0 -4.0 -3.0 10.0 -9.0 3.0 2.0 37.0 -19.0 -1.0 -16.0 11.0 7.0 14.0 -2.0 11.0 -10.0 -5.0 10.0 -7.0 5.0 2.0 26.0 -22.0 -1.0 -10.0 11.0 5.0 26.0 -1.0 11.0 -16.0 -7.0 10.0 -5.0 7.0 2.0 14.0 -19.0 -2.0 -4.0 8.0 3.0 37.0 -1.0 8.0 -21.0 -9.0 10.0 -3.0 9.0 2.0 6.0 -11.0 -2.0 46.0 10.0 43.0 -106.0 22.0 -36.0 78.0 33.0 413.0 5.0 78.0 -192.0 -81.0 91.0 -33.0 81.0 22.0 121.0 -184.0 19.0 -85.0 106.0 53.0 335.0 11.0 106.0 -142.0 -69.0 91.0 -53.0 69.0 22.0 228.0 -213.0 16.0 -142.0 106.0 69.0 228.0 16.0 106.0 -85.0 -53.0 91.0 -69.0 53.0 22.0 335.0 -184.0 11.0 -192.0 78.0 81.0 121.0 19.0 78.0 -36.0 -33.0 91.0 -81.0 33.0 22.0 413.0 -106.0 5.0 43.0 22.0 91.0 
JA: 0 1 3 4 5 1 2 3 4 5 2 3 4 5 3 4 5 6 7 8 4 5 6 7 8 5 6 7 8 6 7 8 9 10 11 7 8 9 10 11 8 9 10 11 9 10 11 12 13 14 10 11 12 13 14 11 12 13 14 12 13 14 13 14 15 16 17 18 19 20 16 17 18 19 20 17 18 19 20 18 19 20 21 22 23 19 20 21 22 23 20 21 22 23 21 22 23 24 25 26 22 23 24 25 26 23 24 25 26 24 25 26 27 28 29 25 26 27 28 29 26 27 28 29 27 28 29 28 29 29 
IA: 0 5 10 14 20 25 29 35 40 44 50 55 59 62 63 64 70 75 79 85 90 94 100 105 109 115 120 124 127 129 130 
----------------------------------------------- 
Разложил её 100.000 раз IC_Decomposition
Время решения: 5.187 сек
Разложил её 100.000 раз IC_Decomposition2
Время решения составило всего 0.828 сек.
Как вы видите IC_Decomposition2 практически в 6 раз быстрее решает чем IC_Decomposition.
А при ещё большем увеличении размерности задачи этот разрыв будет ещё больше увеличиваться.

И тут возникла 1-ая проблема:

Если использовать в качестве предобуславливателя IC_Decomposition (мой) , то уйма времени решения всей задачи уходит только на само разложение, зато этот алгоритм обеспечивает быструю сходимость CG.
Если использовать в качестве предобуславливателя itl - овский IC_Decomposition2, то время на разложение практически не затрачивается , но из – за того что это разложение отличается от точного неполного разложения Холецкого процесс сходимости CG затягивается. Вот если бы сделать так чтобы IC_Decomposition2 давал точное разложение.
vitaly333
Сообщения: 4
Зарегистрирован: 05 апр 2007, 19:07

Теперь 2-ая и самая главная проблема:

При тестировании нескольких реальных задач наткнулся на очень негативный эффект Неполного разложения Холецкого. На некоторых плохо обусловленных симметричных положительно определенных матрицах неполное разложение Холецкого (как моё так и Itl - овское) вообще не выполняется.Это происходит из – за того что в мы IC жестко пренебрегаем появлением новых ненулевых элементов в тех позициях матрицы, где раньше стояли нули , то при вычислении диагональных элементов значение подкоренного выражения становится намного меньше того , если бы мы учитывали все новые появившиеся ненулевые элементы. И поэтому во многих задачах под корнем получаются отрицательные значения и такое разложение не выполняется. Что делать в таких ситуациях???
Вот пример такой матрицы
Ответить