Qual é a melhor maneira de determinar o número de não zeros na multiplicação de matriz esparsa?


17

Eu queria saber se existe um método rápido e eficiente para encontrar antecipadamente o número de não-zeros para operações de multiplicação de matrizes esparsas, assumindo que ambas as matrizes estejam no formato CSC ou CSR.

Eu sei que existe um no pacote smmp, mas preciso de algo que já esteja implementado em C ou C ++.

Qualquer ajuda será apreciada. Desde já, obrigado.


suas matrizes têm simetria ou estrutura para a localização de suas entradas diferentes de zero?
Godric Seer

O problema é que a maioria das pessoas não sabe o que é zerar o zeros, mas a maioria das pessoas não sabe o que é zerar o zíper e o que é zerado, o que faz com que o zig zag seja diferente do zerado.
Recker

Pessoalmente, não consigo pensar em nenhuma maneira de calcular esse número que seria de ordem mais baixa do que apenas fazer a multiplicação de matrizes real sem explorar alguma simetria ou estrutura. Estou assumindo que você deseja isso para alocação de memória antes de fazer a multiplicação?
Godric Seer

Além disso, encontrei este artigo que descreve como estimar o número em um produto de matriz booleana (que é idêntico à contagem dos elementos em qualquer produto de matriz).
Godric Seer

@ GodricSeer..Sim, você está certo, eu preciso do número exato apenas para a alocação de memória da matriz resultante. Obrigado pelo link para o papel, porém.
Recker

Respostas:


14

Você pode apenas simular o produto matriz-matriz, formando o produto dos dois padrões de escarsidade - ou seja, você considera o padrão de esparsidade (armazenado em matrizes separadas no formato CSR) como uma matriz que contém zero ou um em cada entrada. A execução deste produto simulado requer apenas a formação do eoperação nesses zeros e uns e, portanto, é muito mais rápido que o produto matriz-matriz real - na verdade, tudo o que você precisa fazer é percorrer as linhas e colunas das duas matrizes e verificar se há pelo menos uma entrada em um linha e coluna com a qual você se multiplica, onde ambas as matrizes são diferentes de zero. Essa é uma operação barata - muito mais barata do que realmente a multiplicação de ponto flutuante no produto real, que exige não apenas que você faça aritmética de ponto flutuante (caro), mas também leia os números reais de ponto flutuante da memória ( ainda mais caro, mas você não precisa disso ao multiplicar o padrão de dispersão, porque os valores diferentes de zero da matriz são armazenados separadamente no CSR).


6
Isso é chamado multiplicação simbólica. Não é necessariamente menos caro que a multiplicação numérica, especialmente em paralelo, mas precisa ser feito apenas uma vez por padrão de escarsidade. Muitos algoritmos farão a operação várias vezes com valores numéricos diferentes, mas com o mesmo padrão de escarsidade; nesse caso, a multiplicação simbólica pode ser reutilizada.
Jed Brown

É uma boa idéia, mas, considerando os milhões de transistores que estão fazendo o float * flutuar em paralelo, estamos falando apenas de uma economia de velocidade de 50% ou mais aqui.
Evgeni Sergeev

11
@ EvgeniSergeev - o ponto não é a economia nos cálculos, mas a economia na transferência de memória. Como você gasta hoje 80% ou mais de tempo na transferência de memória para uma multiplicação de matriz esparsa, provavelmente obtém um ganho significativo se não precisar ler / gravar dados de ponto flutuante da / para a memória.
Wolfgang Bangerth

Você indicaria explicitamente a complexidade do seu método. Se é por , parece-me que seu método requer trabalho , correto? CmkO(mk)
Carl Christian

@CarlChristian - eu teria que trabalhar nos detalhes, mas certamente não pode ser . Ele precisa envolver o número de não zeros por linha. Se você tem, em média, nonzeros em cada linha e, por simplicidade, se tiver , imagino que você possa implementar o método em algo como ou similar. Isso é muito melhor que . O(mk)pm=kO(mpregistrop)O(m2)
Wolfgang Bangerth

13

Na verdade, eu escrevi o código original no Matlab para A * B, A e B esparsos. A pré-alocação de espaço para o resultado foi realmente a parte interessante. Observamos o que Godric aponta - que conhecer o número de não-zeros na AB é tão caro quanto a computação da AB.

Fizemos a implementação inicial do escasso Matlab por volta de 1990, antes do artigo de Edith Cohen que dava a primeira maneira prática e rápida de estimar com precisão o tamanho da AB. Reunimos um estimador de tamanho inferior e, se ficarmos sem espaço no meio da computação, dobramos a alocação e copiamos o resultado parcialmente calculado.

Não sei o que há no Matlab agora.

Outra possibilidade seria calcular AB uma coluna por vez. Cada coluna pode ser armazenada temporariamente em um acumulador esparso (consulte o artigo esparso do Matlab para obter uma explicação sobre isso) e espaço alocado para armazenar o tamanho exatamente conhecido da coluna de resultados. O resultado seria na forma de coluna dispersa e compactada dispersa - cada coluna no CSC, mas sem contiguidade entre colunas - usando 2 vetores de numcols de comprimento (início da coluna, comprimento da coluna), em vez de um, como metadados. É uma forma de armazenamento que pode valer uma olhada; tem outra força - você pode aumentar uma coluna sem realocar toda a matriz.


Bem, para minha implementação GPU, acabei encontrando a estrutura diferente de zero em primeiro lugar e, em seguida, encontrar o matrix.Performance real era horrível como expected.I pensam que usar o método descrito no presente livro a eficiência multiplicar as duas matrizes esparsas em MATLAB.
Recker

2
Muito legal, obrigado pela perspectiva histórica e bem-vindo ao scicomp :)
Aron Ahmadia

4

Este artigo descreve um algoritmo para aproximar o tamanho de uma resultante do produto da matriz de duas matrizes esparsas.

O problema de encontrar um número exato de entradas diferentes de zero em uma multiplicação de matriz esparsa é que cada elemento resultante depende da interação de dois vetores, sendo provável que ambos contenham pelo menos alguns elementos diferentes de zero. Portanto, para calcular o número, é necessário avaliar as operações lógicas em um par de vetores para cada elemento no resultante. O problema disso é que requer um número de operações semelhante ao número de operações necessárias para calcular o próprio produto da matriz. Nos meus comentários, mencionei a possibilidade de explorar certas estruturas nos elementos diferentes de zero das matrizes originais, no entanto, essas mesmas explorações poderiam ser usadas para reduzir também o trabalho realizado na multiplicação de matrizes.

Provavelmente, seria melhor usar o papel acima para superestimar os requisitos de memória, fazer a multiplicação e truncar a memória alocada ou mover a matriz resultante para uma matriz de tamanho mais apropriado. Além disso, produtos de matriz esparsa não são uma ocorrência rara e eu quase garantiria que esse problema já havia sido resolvido antes. Uma pequena escavação em algumas bibliotecas de matriz esparsas de código aberto deve levar você aos algoritmos que eles usam para pré-alocar memória.


0

Para CSR ou CSC, você tem certeza de que sua matriz de elementos da matriz já não possui zeros? Nesse caso, é simples descobrir quantos elementos diferentes de zero existem, usando algo semelhante a:

int nnz = sizeof(My_Array)/sizeof(long int);

No entanto, se esse não for o caso (parece um pouco fácil), o que você pode tentar é uma redução . Se sua matriz de elementos da matriz for muito grande, talvez seja a maneira mais eficiente de calcular o número de elementos diferentes de zero. Muitas bibliotecas C / C ++ paralelas, como Thrust (uma biblioteca CUDA) ou OpenCL (que você não precisa de uma GPU para usar) têm suporte para reduções condicionais - para cada elemento, adicione o resultado de Condition(Element). Se você definir a condição para Element != 0, adicionará o número de elementos diferentes de zero. Você também pode remover os elementos com valor zero de sua matriz de elementos, matriz de índices de linha / coluna e ajustar os ponteiros de coluna / linha.


obrigado pela sua resposta ... mas eu estava me referindo a não zeros em A * B, onde A e B são matrizes esparsas. Preciso do número de não-zeros com antecedência para poder alocar a quantidade exata de memória para armazenar a matriz resultante.
Recker

0

A maneira mais simples de implementar a RSE é tentar

std::vector< std::map<int, complex<float>> > 

para representar sua matriz. Nesse caso, você realmente não se preocupará com o número de elementos diferentes de zero, tudo será acessado via

std::map< int, complex<float> >::iterator

em cada linha. Melhor ..


2
STL, para quando você pensava que suas rotinas esparsas de matriz não podiam ser mais lentas.
precisa
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.