Tensor Logic
Fonte: source/classes/tensorlogic.prg, source/function/fwtensor.c
FiveWin implementa Tensor Logic: The Language of AI de Pedro Domingos (arXiv:2510.12269). A ideia-chave: uma regra lógica e uma soma de Einstein são a mesma operação. Um programa é um conjunto de equações tensoriais; cada equação faz o join dos tensores do lado direito (produto sobre índices compartilhados), projeta os índices que não aparecem no lado esquerdo (soma) e aplica opcionalmente uma não-linearidade. Um mesmo motor executa redes neurais (atenção, MLPs) e raciocínio simbólico (Datalog sobre tabelas DBF) — e pode diferenciar qualquer programa automaticamente.
Y[i] = f( W[i,j] * X[j] )"] --> N[Neural: atenção, MLP, transformer] EQ --> S[Simbólico: regras Datalog sobre DBFs] EQ --> G["Autodiff: gradiente =
mesmo einsum, fatores trocados"]
Início rápido
- Compile a biblioteca FWH com sua variante (p.ex.
mfwh_new.bat hb32) para quetensorlogic.prgefwtensor.cestejam na lib. - A partir de
samples\ai, compile e execute o companheiro do tutorial:
A saída fica emcd /d C:\fwteam\samples\ai C:\fwteam\samples\build_new.bat tltutor hb32 tltutor.exetltutor.log. Leia-a junto com o tutorial passo a passo. - Velocidade opcional: coloque
libopenblas.dllno PATH ou junto ao exe (ou definaFWT_BLAS_DLL). Verifique com? FWT_BlasActive().
Equações tensoriais
oTL := TTensorLogic():New()
oTL:Let( "W", { { 1, -1, 0 }, { 0, 1, -1 } } ) // liga tensores (arrays o FW_Tensor)
oTL:Let( "X", { 1, 2, 0.5 } )
oTL:Let( "B", { 2, -2 } )
oTL:Eq( "Y[i] = step( W[i,j] * X[j] + B[i] )" ) // um perceptron, uma equacao
oTL:Run() // forward chaining até ponto fixo
? oTL:GetArray( "Y" ) // {1, 0}
Índices repetidos à direita são joinados; os ausentes à esquerda são somados.
Não-linearidades: step, sigmoid, relu, tanh,
gelu, softmax (último índice), lnorm (layer norm, último índice,
sem parte afim — gamma/beta são expressas como equações adicionais). Fatores numéricos escalam
um termo: "S[p,q] = Q[p,k] * K[q,k] * 0.7071".
Sintaxe de equações
| Forma | Significado |
|---|---|
Out[i,k] = A[i,j] * B[j,k] | Join em j, projeta j (produto matricial). |
Y[i] = step( W[i,j] * X[j] + B[i] ) | Vários termos aditivos + não-linearidade. |
S[p,q] = Q[p,d] * K[q,d] * 0.7071 | Factor numérico de escala en um termo. |
A[p,q.] = softmax( S[p,q] ) | O . final marca o eixo do softmax. |
N[p,d] = lnorm( R[p,d] ) | Layer norm sobre o último índice. |
N2[p,d] = N[p,d] * G[d] + Ones[p] * B[d] | Parte afim gamma/beta após lnorm. |
Termos com qualquer número de fatores são avaliados encadeando contrações em pares: só sobrevivem as letras ainda necessárias para a saída, de modo que cada passo pode usar o caminho rápido GEMM do OpenBLAS.
Se o parse falhar, Eq() retorna .F. e preenche oTL:cError.
TTensorLogic
| Método | Descrição |
|---|---|
New() | Cria um programa vazio (tensores, equações, estado Adam). |
Let( cName, uData ) | Liga um tensor (array aninhado de qualquer rank ou FW_Tensor). |
Param( cName, uData ) | Let + marcar como treinável (atualizado por Step() / StepAdam()). |
Eq( cEquation ) | Adiciona uma equação tensorial (string). Retorna .F. se não parsear. |
Run( nMaxPasses, nTol ) | Forward chaining até ponto fixo (padrão 100 passadas, tol 1e-6). Regras Datalog recursivas convergem. |
Forward() | Uma passagem em ordem de declaração; cacheia pré-ativações para Backward(). Use ao treinar, não Run(). |
ZeroGrad() | Zera todos os gradientes acumulados. |
Seed( cName, tGrad ) | Define dL/dName (gradiente da perda em relação a um tensor de saída). |
Backward() | Varredura inversa. O gradiente de uma equação = mesmo einsum com fator e saída trocados. |
GetGrad( cName ) | Retorna dL/dName como FW_Tensor. |
Step( nLr ) | SGD: cada Param -= lr * grad. |
StepAdam( nLr, nB1, nB2, nEps ) | Otimizador Adam (kernel C FWT_AdamUpdate com correção de viés). Padrão: β1=0.9, β2=0.999, ε=1e-8. |
ClipGrads( nMaxNorm ) | Clipping global L2 de gradientes. Retorna a norma pré-clip. |
ClearActGrads() | Remove gradientes de ativações, mantém os de parâmetros (acumulação por mini-batch). |
SaveParams( cFile, hUser ) / LoadParams( cFile ) | Checkpoint: cada tensor Param() como float32 crudo más un hash opcional de usuario. Load retorna el hash o NIL si falla. |
Get( cName ) / GetArray( cName ) | Tensor resultado / array aninhado (2D). |
LetDbf( cName, cAlias, aFields, oDomain ) | Registros DBF → tensor booleano de relação (uma dimensão por campo). |
LetSet( cName, aEntities, oDomain ) | Lista de nomes → tensor booleano unário. |
Query( cName, oDomain, nThresh ) | Tensor resultado → tuplas de nomes (limiar padrão 0.5). |
DATA: hTensors, aEqs, cError, hPre, hGrads,
aParams, hAdamM, hAdamV, nAdamT.
Padrões de loop de treino
// Amostra única / batch completo
oTL:Forward()
oTL:ZeroGrad()
oTL:Seed( "Y", FWT_Scale( FWT_Sub( oTL:Get( "Y" ), tTarget ), 2 ) ) // d MSE
oTL:Backward()
oTL:ClipGrads( 1.0 )
oTL:StepAdam( 0.05 )
// Acumulação por mini-batch (estilo TTransformerTL)
oTL:ZeroGrad()
for i := 1 to nBatch
oT:AccumStep( aIds[i], aTgt[i], 1 / nBatch ) // grads de params somam
next
oTL:ClipGrads( 1.0 )
oTL:StepAdam( nLr )
TTLDomain
Mapa entidade ↔ índice compartilhado por todas as relações para que as dimensões coincidam.
| Método | Descrição |
|---|---|
New() | Domínio vazio. |
Add( cName ) | Registra el nombre si es nuevo; retorna id 1-based. |
Id( cName ) | Id ou 0 se não existir. |
Name( nId ) | Nome do id. |
Len() | Número de entidades. |
Harvest( cAlias, aFields ) | Registra todos os valores desses campos no alias aberto. Chamar antes de LetDbf(). |
TTransformerTL
Fachada transformer estilo GPT sobre um programa de tensor logic (~15 equações por camada).
As cabeças são um índice (sem fatiar colunas): WQ[d,h,k] absorve o repart
por cabeça e WO[h,k,d] absorve concat+Wo. Com os mesmos pesos reproduz
ForwardSeq() da classe Transformer clássica de perto, e
treina e gera com o autodiff genérico — zero código de backprop específico.
| Método | Descrição |
|---|---|
New( nLayers, dModel, nHeads, nVocab ) | Constrói o programa + inicializa parâmetros (estilo Xavier). Padrão: 1, 8, 2, 4. |
BuildProgram() | Emite residual, atenção multi-cabeça, FFN e projeção para vocabulário como equações. |
InitParams() | Tensores Param() aleatórios (reproduzíveis) para todos os pesos. |
LoadFromTransformer( oT ) | Copia pesos de um Transformer clássico (testes de paridade). |
SetInput( aIds ) | One-hot X, máscara causal, Pos, vetores de uns. |
ForwardSeq( aIds ) | → array aninhado de probs [seq][vocab]. |
TrainStep( aIds, aTargets, nLr ) | Passo de perda CE (ZeroGrad + Backward + Step). Retorna a perda. |
AccumStep( aIds, aTargets, nScale ) | Só acumula gradientes (para mini-batches). Retorna a perda escalada. |
EvalLoss( aIds, aTargets ) | CE só forward (validação). |
Generate( aSeed, nNew ) | Geração greedy argmax de nNew tokens. |
oTLT := TTransformerTL():New( 2, 8, 2, nVocab )
aProbs := oTLT:ForwardSeq( { 2, 3, 4, 5, 6 } )
nLoss := oTLT:TrainStep( aIds, aTargets, 0.01 )
aOut := oTLT:Generate( { 1 }, 8 )
// multi-batch:
oTLT:oTL:ZeroGrad()
for i := 1 to nBatch
oTLT:AccumStep( aWin[i], aTgt[i], 1 / nBatch )
next
oTLT:oTL:ClipGrads( 1.0 )
oTLT:oTL:StepAdam( nLr )
Esqueleto de atenção como equações (de BuildProgram()):
oTL:Eq( "Q[h,p,k] = H0[p,d] * WQ[d,h,k]" )
oTL:Eq( "S[h,p,q] = Q[h,p,k] * K[h,q,k] * 0.5 + OnesH[h] * Mask[p,q]" )
oTL:Eq( "A[h,p,q.] = softmax( S[h,p,q] )" )
oTL:Eq( "O[h,p,k] = A[h,p,q] * V[h,q,k]" )
oTL:Eq( "Att[p,d] = O[h,p,k] * WO[h,k,d]" )
oTL:Eq( "N1[p,d] = lnorm( Att[p,d] + H0[p,d] )" ) // residual + layer norm
// ... FFN, segunda norma, proyeccion a vocabulario, softmax
Datalog sobre tabelas DBF
USE tlfam ALIAS FAM NEW // campos PARENT, CHILD
oDom := TTLDomain():New()
oDom:Harvest( "FAM", { "PARENT", "CHILD" } ) // fixar o domínio primeiro
oTL:LetDbf( "Parent", "FAM", { "PARENT", "CHILD" }, oDom )
oTL:LetSet( "Male", { "abe", "homer" }, oDom )
oTL:Let( "NotEq", TL_NotEq( oDom:Len() ) )
oTL:Let( "Ancestor", FWT_New( { oDom:Len(), oDom:Len() } ) )
oTL:Eq( "Father[x,y] = step( Parent[x,y] * Male[x] )" )
oTL:Eq( "Sibling[x,y] = step( Parent[p,x] * Parent[p,y] * NotEq[x,y] )" )
oTL:Eq( "Ancestor[x,y] = step( Parent[x,y] + Ancestor[x,z] * Parent[z,y] )" ) // recursiva
oTL:Run( 20 ) // ponto fixo
AEval( oTL:Query( "Ancestor", oDom ), {| a | QOut( a[1] + " -> " + a[2] ) } )
Raciocínio em espaço de embeddings (a ponte neuro-simbólica do paper): entidades como vetores unitários, a relação embutida, consulta inversa e sigmoide com temperatura — T→0 dedutivo (0/1 duro), T alto analógico:
oE:Let( "Emb", TL_UnitEmb( n, 256, 1.234 ) )
oE:Eq( "EmbR[i,j] = Parent[x,y] * Emb[x,i] * Emb[y,j]" )
oE:Eq( "Score[x,y] = EmbR[i,j] * Emb[x,i] * Emb[y,j]" ) // ~1 se (x,y) está em Parent
tSoft := FWT_Sigmoid( tShifted, nTemperature )
Helpers (PRG)
| Função | Descrição |
|---|---|
TL_TensorND( aNested ) | Array Harbour aninhado (qualquer rank) → FW_Tensor. |
TL_NotEq( n ) | [n,n] uns exceto a diagonal (guarda Datalog x != y). Igual a FWT_NotEq. |
TL_UnitEmb( n, d, nSeed ) | Embeddings unitarios [n,d] (determinísticos). Igual a FWT_UnitEmb. |
TL_Rnd2 / TL_Rnd3 / TL_Const1 | Init estilo Xavier e vetores constantes usados por TTransformerTL. |
Motor C (fwtensor.c)
| Função | Descrição |
|---|---|
FWT_New( aShape ) / FWT_FromArray / FWT_ToArray / FWT_Shape / FWT_Size / FWT_Get / FWT_Set | Criar e inspecionar tensores float32 planos (com GC). |
FWT_EinSum( cSpec, t1, ... ) | Soma de Einstein generalizada, p.ex. "ij,jk->ik", "pd,qd->pq", até 6 operandos. A primitiva do tensor logic. |
FWT_MatMul / FWT_Add / FWT_AddBias / FWT_Scale / FWT_Transpose | Álgebra linear básica (MatMul usa BLAS se disponível). |
FWT_SoftmaxLast / FWT_LNormLast | Softmax / layer norm sobre a última dimensão, qualquer rank. |
FWT_Softmax / FWT_LayerNorm | Variantes 2D por linhas (caminho GPT-2 clássico). |
FWT_Sigmoid( t, nT ) / FWT_Step( t, nThresh ) / FWT_ReLU / FWT_Tanh / FWT_GELU | Não-linearidades elemento a elemento; sigmoid aceita a temperatura de raciocínio. |
FWT_ActDeriv / FWT_SoftmaxBackwardLast / FWT_LNormBackwardLast | Kernels backward usados por TTensorLogic:Backward(). |
FWT_Mul / FWT_Sub / FWT_Fill / FWT_Clone / FWT_MaxDiff | Operações elementwise e utilitários. |
FWT_AdamUpdate / FWT_SumSq / FWT_ScaleInPlace | Passo Adam, normas de gradiente, clipping. |
FWT_GetBytes / FWT_SetBytes | Buffer float32 bruto como string binário (checkpointing). |
FWT_OneHot( aIds, nVocab ) | [len,vocab] one-hot a partir de ids 1-based. |
FWT_CausalMask( nSeq, nVal ) | Máscara aditiva de atenção (0 em/abaixo da diagonal, nVal acima; padrão −1e9). |
FWT_SinPos( nSeq, nDim ) | Codificação posicional sinusoidal [seq,dim]. |
FWT_NotEq( n ) / FWT_UnitEmb( n, d, nSeed ) | Builders C para helpers Datalog / embeddings. |
FWT_GatherRows / FWT_SliceCols / FWT_SetCols | Lookup de embeddings e vistas de colunas (caminho transformer clássico). |
FWT_LoadSafe( cFile, nOffset, aShape ) | Lê um tensor float32 de um blob estilo safetensors. |
FWT_BlasActive() | .T. quando há backend OpenBLAS sgemm carregado. |
Aceleração OpenBLAS
fwtensor.c carrega OpenBLAS dinamicamente no primeiro uso — nunca é uma
dependência obrigatória: libopenblas.dll no PATH ou junto ao exe, o la variable de entorno
FWT_BLAS_DLL (com off força os loops C portáveis; também detecta builds
ILP64 como libscipy_openblas64_ do numpy). FWT_MatMul e todo
FWT_EinSum de 2 operandos redutível a um GEMM (em lote) roda sobre
cblas_sgemm, usando as transposições do BLAS para que os padrões de atenção não
precisem de reempacotamento.
| Padrão (Xeon W-2140B, 8c) | Loops C | OpenBLAS | PyTorch 2.11 / MKL |
|---|---|---|---|
| matmul 1024x1024x1024 | 0.32 GFLOPS | 497 GFLOPS | 982 GFLOPS |
| matmul 512x512x512 | 0.32 GFLOPS | 290 GFLOPS | 806 GFLOPS |
| attention scores p=512 d=64 | 0.31 GFLOPS | 55 GFLOPS | 466 GFLOPS |
O OpenBLAS do numpy alcança 446 GFLOPS no matmul de 1024 na mesma máquina — o einsum do FWH iguala o desempenho classe BLAS; a distância restante ao PyTorch são os kernels AVX-512 do MKL e menor overhead por chamada, não a linguagem.
Exemplos
samples/ai/tltutor.prg— companheiro executável do tutorial (lições 1–9 →tltutor.log).samples/ai/tensorlogic.prg— suíte do motor: einsum vs matmul, cabeça de atenção em 6 equações, ponto fixo Datalog, verificações de gradiente vs diferenças finitas, treino XOR (9 PASS).samples/ai/tltransformer.prg— transformer como equações, paridade com a classe clássica, treino + geração (3 PASS).samples/ai/tldatalog.prg— Datalog sobre um DBF familiar + raciocínio em embeddings com temperatura (6 PASS).samples/ai/tlbench.prg— benchmark de einsum, BLAS vs loops C.samples/ai/tlminillm.prg— mini-LLM estilo DeepSeek/MiMo como equações puras: atenção latente MLA, RoPE, mixture-of-experts, predição multi-token (3 PASS).samples/ai/tlfwcode.prg— modelo de linguagem char-level sobre fontes FWH reais: Adam + warmup/cosine, acumulação por lotes, clipping, split train/val, checkpoint do melhor-val com resume, sampling top-k. Configurável comTLFW_*; resume comTLFW_RESUME=1.samples/ai/tlc.prg— receita de treino sintético de código em miniatura: gerador + oráculo exato, aprendizado de execução (36–40% vs 10% acaso) e decoding com máscara gramatical (100% válido mesmo sem treinar).
Compilar qualquer exemplo da mesma forma:
cd /d C:\fwteam\samples\ai
C:\fwteam\samples\build_new.bat <nombre> hb32
<nombre>.exe