Notação de Dirac: álgebra linear em hardware

O recurso mais singular do C±: vetores e matrizes se operam com a notação bra-ket da física, e cada expressão vira código totalmente desenrolado, sem laços. Um produto interno de 4 elementos gera cerca de dez instruções em linha reta; as dimensões precisam ser constantes conhecidas na compilação, e é exatamente isso que permite o desenrolamento.

As formas suportadas

Com vetores a, b, d, matrizes A, B, M, P e escalares c, g, e:

Escrita

Calcula

e = d - ⟨w|x⟩;

produto interno \(\langle w \mid x \rangle\) dentro de expressão

a # |M|b⟩;

\(a = M b\) (matriz vezes vetor)

a # c|b⟩;

\(a = c\,b\) (escalar vezes vetor)

a # |b⟩ + c|d⟩;

\(a = b + c\,d\) (soma ponderada)

A # |a⟩⟨b|;

\(A = a\,b^{T}\) (produto externo)

A # |B| - |a⟩⟨b|;

\(A = B - a\,b^{T}\)

A # c|B|;

\(A = c\,B\)

A # c|I|;

\(A = c\,I\) (identidade escalada)

a # |0⟩;

zera o vetor

a # c|in(p)⟩;

preenche a lendo a porta p, cada leitura vezes c

a # c -> |a⟩;

registrador de deslocamento: desloca a e insere c

out(p, c|a⟩);

escreve o vetor inteiro na porta, cada elemento vezes c

O # marca a atribuição vetorial. As declarações também aceitam inicialização por Dirac: float Px[4] # |P|x⟩;.

Regras verificadas na compilação: dimensões compatíveis, tipos iguais dos dois lados (int com int, float com float), e nada de comp. O registrador de deslocamento exige o mesmo vetor dos dois lados.

Tutorial: um filtro RLS em 66 linhas

O filtro adaptativo RLS (Recursive Least Squares) é o caso de uso perfeito: seu miolo é puro produto de matriz e vetor, e em C com laços ele viraria uma página de índices. Em Dirac, cada linha do algoritmo matemático vira uma linha de código.

Crie um processador proc_rls (32 bits, mantissa 23, expoente 8, uma porta de entrada e uma de saída, pilha de dados 8) e use o programa:

Listagem 9 proc_rls.cmm, o nucleo do algoritmo
 1#PRNAME proc_rls
 2#NUBITS 32
 3#NBMANT 23
 4#NBEXPO 8
 5#NDSTAC 8
 6#SDEPTH 2
 7#NUIOIN 1
 8#NUIOOU 1
 9
10#define N 4          // ordem do filtro
11
12void main()
13{
14    float x[N];      // entradas recentes
15    float w[N];      // coeficientes do filtro
16    float P[N][N];   // matriz de covariancia inversa
17    float Px[N];
18    float K[N];
19    float d, y, e, g;
20
21    P # 1000.0|I|;               // P = 1000 * identidade
22    w # |0⟩;                     // coeficientes zerados
23
24    while (1)
25    {
26        x # fin(0) -> |x⟩;       // desloca x e insere a amostra nova
27        d = fin(0);              // sinal desejado
28
29        y = ⟨w|x⟩;               // saida do filtro
30        e = d - y;               // erro
31
32        Px # |P|x⟩;              // P x
33        g = 1.0 / (1.0 + ⟨x|Px⟩);
34        K # g|Px⟩;               // ganho de Kalman
35
36        w # |w⟩ + e|K⟩;          // atualiza os coeficientes
37        P # |P| - |K⟩⟨Px|;       // atualiza a covariancia
38        P # 1.0101|P|;           // fator de esquecimento
39
40        fout(0, y);
41    }
42}

Leia o miolo do while ao lado das equações do RLS em qualquer livro de filtragem adaptativa: é uma transcrição. Esse é o argumento do recurso.

Editor com o codigo RLS em notacao de Dirac, com os brackets realcados.

Figura 18 O editor entende os brackets: realce e espaçamento próprios para a notação.

Para digitar e , use os caracteres U+27E8 e U+27E9 (o editor os aceita normalmente; vale montar atalhos de teclado do sistema ou copiar do próprio código de exemplo).

Quando usar

Dirac compensa quando o algoritmo é álgebra linear de dimensão pequena e fixa: filtros adaptativos, projeções, transformações de coordenadas, redes pequenas. Para dimensões grandes, o desenrolamento explode o número de instruções; o compilador avisa o tamanho gerado, e o relatório do TASM mostra o total. A alternativa nesses casos é escrever os laços em C± comum.