← ONTO GitLab

onto-rs

Implementazione Rust del linguaggio ONTO (Programmazione Orientata alle Proprietà, logica trivalente V/F/N) con motore database unificato: il linguaggio e il database sono lo stesso sistema — eseguire uno script .onto popola direttamente un motore trivalente indicizzato (OntoEngine), interrogabile nello stesso script.

Il paradigma è puro mereologico:

  • Elementi = aggregati liberi di proprietà, senza schemi né classi dichiarate a priori: [ V=febbre, "Mario"=nome ] = mario.
  • Classi = aggregati intensionali lazy: class { V=febbre ^ V=tosse } = Febbricitanti. La membership non si dichiara, si calcola sui bitset a ogni interrogazione; i nuovi elementi entrano nelle classi automaticamente; class fixed congela uno snapshot.
  • Trivalenza = tre valori di verità, V/F/N, con le tavole della logica ontologica: negazione asimmetrica, congiunzione come compressione (V ^ N = F, F ^ N = N), implicazione =>, coimplicazione <=>, xor. Un valore indeterminato (N) entra nel ragionamento invece di essere silenziato da un default booleano (vedi docs/LOGICA.md). La negazione stretta ~ isola, quando serve, una porzione che si comporta come la logica classica.

Quando usarlo

ONTO eccelle quando i dati sono profili booleani con valori mancanti che contano, le query sono congiunzioni di proprietà e ranking per punteggio, e il deployment è un processo Rust embeddato. Per tutto il resto esistono strumenti migliori: la guida alla scelta, con diagramma decisionale, use case concreti e limiti onesti, è in docs/USE_CASES.md.

Documentazione

  • docs/LOGICA.md — logica ontologica: tavole canoniche, identità epistemica, leggi a confronto con la logica booleana, proprietà metalogiche
  • docs/LEGGI_LOGICHE.md — derivazione computazionale delle leggi (generato da strumenti/leggi_logiche.py)
  • docs/METALOGICA.md — correttezza, completezza, decidibilità, conservatività, adeguatezza funzionale e struttura modale (generato da strumenti/metalogica.py)
  • docs/LINGUAGGIO.md — riferimento del linguaggio
  • docs/ARCHITETTURA.md — design di motore e interprete
  • docs/BENCHMARK.md — benchmark comparativo vs SQLite/sled/scan naive
  • docs/USE_CASES.md — guida alla scelta: quando usare ONTO e quando no

Quick start

sh
# Build e test
cargo build --release
cargo test

# Esegue uno script
cargo run -p onto-cli -- run examples/diagnosi.onto

# REPL interattiva
cargo run -p onto-cli -- repl

# Persistenza: salva lo stato del motore su file e lo ricarica in un run successivo
cargo run -p onto-cli -- run examples/diagnosi.onto --save stato.bin
cargo run -p onto-cli -- run examples/negozio.onto --load stato.bin --save stato.bin
# nella REPL: ':save stato.bin' e ':load stato.bin'

# Stato persistente con log di scrittura: ripristinato all'avvio, ogni
# elemento e relazione registrati prima di essere applicati, checkpoint a fine run;
# rieseguire lo stesso script non reinserisce i suoi elementi
cargo run -p onto-cli -- run examples/diagnosi.onto --store dati/
cargo run -p onto-cli -- repl --store dati/      # ':checkpoint' compatta il log

# Servizio di rete: ogni POST esegue il sorgente ONTO nel corpo della richiesta
cargo run -p onto-cli -- serve --port 7878 --store dati/ --workers 4
curl -X POST http://127.0.0.1:7878/ --data-binary '[ "Mario"=nome, V=febbre ] = mario'
curl -X POST http://127.0.0.1:7878/leggi --data-binary 'top_k{ 5=k } = t : list
"{t}" : print'

# Con cifratura e token
cargo run -p onto-cli -- serve --tls-cert cert.pem --tls-key chiave.pem --token segreto

# Gruppo di tre nodi con sostituzione automatica del primario
cargo run -p onto-cli -- serve --port 7001 --store n1/ --node host1:7001 --replica host2:7002 --replica host3:7003
cargo run -p onto-cli -- serve --port 7002 --store n2/ --node host2:7002 --primary host1:7001 --replica host1:7001 --replica host3:7003

# Dati ripartiti tra due gruppi: la chiave sceglie il gruppo
cargo run -p onto-cli -- route --port 7800 --group host1:7001,host2:7002,host3:7003 --group host4:7001
curl -X POST http://127.0.0.1:7800/ -H 'X-Onto-Chiave: mario' --data-binary '[ "Mario"=nome, V=febbre ] = mario'

# SQL: via HTTP, o con i client di PostgreSQL (la parola d'ordine è il token)
cargo run -p onto-cli -- serve --port 7878 --pg-port 5433 --token segreto
curl -X POST http://127.0.0.1:7878/sql -H 'Authorization: Bearer segreto' --data-binary "SELECT nome FROM elementi WHERE febbre = V"
psql "host=127.0.0.1 port=5433 user=onto password=segreto dbname=onto"

# Aggiungere un gruppo: l'instradatore in funzione divide in due i dati del gruppo 0
cargo run -p onto-cli -- split --router host0:7800 --split 0=host5:7001
# I nodi ricordano la disposizione nuova: gli instradatori la adottano da soli

# Dati più grandi della RAM: la memoria del processo sta su un file mappato
cargo run -p onto-cli -- --memory-file memoria.bin --memory-file-gib 256 run examples/negozio.onto

# Benchmark comparativo (sweep Q1-Q8 su 4 motori, CSV in uscita)
cargo run -p onto-cli --release -- bench --sizes 1000,10000,100000,500000 --out risultati_bench.csv

# Micro-benchmark di regressione (criterion)
cargo bench

Esempio: il linguaggio come database

onto
[ V=febbre, V=tosse ] = mario
[ V=febbre, F=tosse ] = anna

# La classe è intensionale: si calcola lazy sul motore popolato dagli
# elementi sopra — un elemento inserito dopo la definizione vi entra
# automaticamente se soddisfa il selettore.
class { V=febbre ^ V=tosse } = Febbricitanti
count{ Febbricitanti=class } = n : int   # 1

# Membership: V o F, mai un default silenzioso.
if (mario in Febbricitanti) {
    "mario è febbricitante" : print
}
if (anna in Febbricitanti) {
    "anna è febbricitante" : print        # non stampato: F
} else {
    "anna NON è febbricitante" : print
}

# search_val/search_profile/top_k/distribution interrogano lo stesso motore.
search_profile{ V=febbre, V=tosse } = con_entrambi : list
"febbre e tosse (id nel motore): {con_entrambi}" : print   # [0]

Struttura del workspace

onto-rs/
├── src/
│   ├── core/       tipo trivalente (TrivalentValue), MOVP (aggregato di proprietà) e peso epistemico (V=2,N=1,F=0)
│   ├── storage/    indice bitset trivalente (TrivalentIndex) + OntoEngine (motore unificato)
│   ├── graph/      grafo di relazioni tipate, integrato in OntoEngine
│   ├── lang/       lexer, parser, AST, interprete tree-walking
│   └── query/      LazyQuery: query trivalenti lazy sopra OntoEngine
├── onto-cli/       binario `onto`: run / repl / bench
├── examples/       script .onto dimostrativi (elementi, classi, motore, domini applicativi)
├── tests/          test d'integrazione (esegue tutti gli esempi)
├── benches/        micro-benchmark criterion
└── docs/           documentazione del linguaggio, architettura, benchmark, use case

Benchmark in sintesi

A N = 500 000 record (8 proprietà booleane trivalenti, 55% V / 30% F / 15% N), ONTO è il motore più veloce su tutte le query testate contro SQLite (rusqlite bundled), uno scan lineare senza indici e sled (KV embedded, usato come proxy di Redis). Tempi in ms:

Query onto sqlite speedup sqlite/onto
Q1 scan singolo 3.87 22.42 5.8×
Q2 AND 3 proprietà 1.40 57.29 41×
Q3 AND 6 proprietà 1.10 55.88 51×
Q5 distribuzione V/F/N 0.017 15.82 ~950×
Q7 top-20 per peso epistemico 8.36 155.9 19×
Q8 scan a soglia 3.76 408.6 109×

Metodologia, tabelle complete per ogni N e note nella docs/BENCHMARK.md.

Stato di produzione

  • Copre l'intera superficie del linguaggio usata dagli script in examples/, incluso motore_db.onto (funzioni built-in di interrogazione del motore) e classi.onto (classi dinamiche, fisse, aggregate, membership).
  • Test: cargo test (unit + integrazione sugli esempi); qualità statica: cargo clippy.
  • Persistenza: OntoEngine::save/load (snapshot binario bincode), esposti da onto run --save/--load e dalla REPL (:save/:load); per non perdere le scritture tra due snapshot c'è lo stato persistente con log di scrittura e replay automatico (storage::MotorePersistente), esposto da onto run --store <dir> e onto repl --store <dir>.
  • Concorrenza: MotoreCondiviso condivide il motore tra thread, con letture in parallelo e scritture in esclusiva.
  • Linguaggio: transazioni (transaction { … }), modifica e ritiro (set_property, retract), classi e ricerche su numeri e testi (valore, intervallo, testo libero per rilevanza con radici e sinonimi), campi unici, join anche con condizione, aggregazioni e raggruppamenti, SQL sulle classi (SELECT con espressioni, funzioni, join, sottointerrogazioni, HAVING, UNION; INSERT, UPDATE, DELETE; information_schema), ricerca per frase, per parole simili e in 18 lingue, ricerca per vicinanza tra vettori, importazione di altri file.
  • Memoria: circa 6 byte per record con 8 proprietà; i record si ricostruiscono dall'indice. Per dati più grandi della RAM, --memory-file tiene tutta la memoria su un file mappato; MotorePartizionato (API Rust) tiene i record in partizioni su disco.
  • Rete: onto serve espone il motore su HTTP o HTTPS, con token di accesso, letture in parallelo, stato su disco (istantanea più diario), replica e sostituzione automatica del primario a maggioranza; onto route ripartisce i dati tra più gruppi di nodi, con scritture «tutti o nessuno», interrogazioni SQL e join tra i gruppi, campi unici tra tutti; onto split aggiunge un gruppo senza fermare il servizio. SQL anche con il protocollo di PostgreSQL (--pg-port): transazioni, WITH RECURSIVE, funzioni a finestra, liste, tabelle con tipi e chiavi esterne, viste, e i cataloghi con cui psql descrive lo schema.
  • Ricerca: per parole in 18 lingue (search_text), per vicinanza tra vettori con indice dei vicini (nearest, vector_index), per parole che condividono i contesti (similar_text).
  • Limiti: in un join tra gruppi una sola tabella resta dov'è; di PostgreSQL ci sono SQL e lo schema, non procedure ed estensioni; oltre la RAM ogni lettura sparsa costa un accesso al disco — onerosità e strategie di deployment in docs/USE_CASES.md § Limiti.

Fonte: onto-rs/README.md