Nel calcolo evolutivo, ladiversitàè il presupposto per l'efficacia dell'algoritmo: solo mantenendo un numero sufficiente di soluzioni candidate la pressione selettiva non spinge prematuramente la popolazione verso un ottimo locale. Ma la diversità comporta allo stesso tempo un enorme carico computazionale: una singola esecuzione richiede spesso di valutare decine di migliaia o persino milioni di soluzioni candidate. Quando i candidati vengono scritti come normali array di oggetti e inviati uno per uno alla valutazione tramite cicli elemento per elemento, la maggior parte del tempo di calcolo viene spesa nella pianificazione elemento per elemento e nel flusso di controllo del linguaggio ospite, anziché nelle vere operazioni numeriche: è proprio questala causa principale dell'inefficienza dei tradizionali cicli seriali su CPU nella gestione di decine di migliaia di valutazioni di candidati.
Il salto nel modo di rappresentare: dalla lista di candidati al grafo computazionale tensoriale
Il cambiamento introdotto da EvoX non è l'accelerazione locale di qualche operatore, maun cambiamento nel modo di rappresentare il processo evolutivo: dal trattamento a lista dei candidati uno per uno si passa a un calcolo batch in cui il tensore è un cittadino di prima classe. L'insieme dei candidati non è più disperso in array di oggetti, ma viene organizzato in modo unitario come tensore insieme ai risultati della valutazione e allo stato dell'algoritmo, così che possano partecipare alle operazioni nel loro complesso.
- Tensore dei candidati: l'intera generazione di candidati viene scritta come tensore di forma [dimensione della popolazione, dimensione della soluzione]. A questo punto "un candidato" è solo una riga del tensore, non più un oggetto indipendente del linguaggio ospite.
- Operatori batch: operazioni come la mutazione agiscono direttamente sull'intera popolazione; una sola chiamata aggiorna tutti i candidati, invece di richiamare N volte in un ciclo.
- Valutazione batch: nella forma evaluate(tensore dei candidati) si ottiene in una sola volta il risultato della valutazione dell'intera generazione; l'unità di misura della valutazione passa da "singolo" a "batch".
- Grafo computazionale: selezione e aggiornamento dello stato non sono più solo logica di controllo seriale, ma nodi del grafo, ottenendo così opportunità di ottimizzazione del compilatore come la fusione degli operatori e la pianificazione parallela.
Confronto tra i due modi di scrivere il codice
Il modo di scrivere a lista è all'incirca: estrarre un candidato alla volta, valutarlo uno alla volta, confrontarlo uno alla volta e aggiornare lo stato — ogni passo dipende dai cicli del linguaggio ospite ed è difficile da ottimizzare nel complesso. Il modo tensoriale invece è: tensore dei candidati → mutazione → valutazione → selezione → aggiornamento dello stato; l'intera catena è composta da operazioni tensoriali ed espressa come un grafo computazionale, e il compilatore può applicare fusione e pianificazione dei dispositivi all'intero grafo. Nota: la logica dell'algoritmo (distribuzione della mutazione, pressione selettiva) non è stata riscritta; ciò che è stato riscritto è ilmodo di rappresentare.
