Repository navigation
Releases: MesTTo/MeTTa
Release list
pymetta 0.9.4
pymetta 0.9.4 and the eighteen distributions under ext/ at 0.9.4, each pinning pymetta==0.9.4, metta-library-pack new among them. tsmetta 0.0.1-alpha.3 is released beside it on the same engine.
pymetta 0.9.3 is deprecated and stays installable: its Linux wheels ship 71 binaries without the notices their licences ask for, GMP, unixODBC, libltdl, libacl, libxcrypt and SWI-Prolog's isub linked into the wheel, OpenSSL 1.1.1k, and sweep-module.so. Install 0.9.4. PyPI can mark a single release only by yanking it, and 0.9.3 is not yanked, so this notice and the changelog carry the deprecation.
Before you upgrade
pymetta's Linux wheels carry no GPL- or LGPL-licensed binary. The SWI-Prolog libraries whose plugins link LGPL code, library(odbc) with library(cql/...), library(crypt) and library(isub), move to the new metta-library-pack distribution, and pip install 'pymetta[pack]' installs the release built from the same host as your pymetta. Without it, loading one of those libraries is refused with that command named. No shipped MeTTa library loads any of them.
The wheels' host computes unbounded integers and rationals with SWI-Prolog's bundled LibBF instead of GMP, as the WebAssembly host always has. Over a differential of 300,000 seeded cases through every path of SWI's arithmetic, a LibBF build differs from a GMP build only in ten ~g or ~G renderings of a big integer at an exact decimal tie, where GMP truncates before it rounds and LibBF rounds the value. LibBF's multiplication and radix conversion are 3 to 5 times slower than GMP's at half a million digits. archive4pl.so links the libarchive 3.8.5 that lib_compression pins, built on OpenSSL 3 without ACL or extended-attribute support.
A SWI-Prolog you built yourself has to be rebuilt. A pymetta host now needs 63 patches to SWI-Prolog where 0.9.3's needed 37: the engine requires 55 to swipl-devel itself and pymetta 8 to janus, and each refuses at boot a host that lacks one, naming each missing patch. On Linux x86_64 under CPython 3.12, 3.13 and 3.14 the wheel carries the patched host, so pip install pymetta is still the whole install; anywhere else, rebuild and redeclare the host as docs/patched-host.md describes.
These change what an existing program sees:
subtract-atom, and with itspace.remove,space -= atomandtransfer, takes an occurrence equal to the atom before an older one that only unifies with it, sos += a; s -= aleaves the space it found, and it no longer binds the caller's pattern.transferlands the atom that left, as the source stored it.- A removal event from
remove,-=,transferandsubtract-atomcarries the atom that left, as the space stored it, where it carried the pattern that was asked for. - A builtin call's answer is final unless the call holds an operand back at its own arity, upstream PeTTa's rule:
(car-atom (quote ((+ 1 2) b)))is(+ 1 2)where it was 3, and the closure spellings ofmap-atom,filter-atomandfoldl-atomanswer what their binder spellings answer. defineinside its own space'swith m.batch():block is refused, asremove,transfer,clearanddeclarethere are, since a define's writes now ride one transaction.- A refusal's fields are typed by what they hold, and a structured one crosses as an atom rather than as text.
Space.algebratakes a Python callable for every operation role, and an operation made from a callable is named for the space that declared it.integrate.module_opsmaps an implicit name's underscores to hyphens, asopdoes, so a module'sadd_oneregistersadd-one.markup-parse-htmlreads lib_markup's own HTML5 DTD, derived from the WHATWG HTML Standard at a pinned commit. Both hosts leave out Jukka Korpela's informal HTML5 DTD, and the Linux wheels OCLC'slibrary/semweb/eor.rdfs, because no terms permitting their redistribution were found;load_html/3parses HTML5 once lib_markup is loaded.- lib_soft's
soft-symbol?is gone: callis-symbol. sweep-module.so, the Emacs module SWI builds with GNU Emacs's GPL-3.0-or-later header, is dropped from every host. Nothing loaded it.
New
metta-library-pack, above: one SWI pack per library,THIRD-PARTY-NOTICESbeside every binary, and the complete source of every LGPL component it links. The seat attaches its packs before the engine boots and refuses packs linked against another host, naming both builds.- Each MeTTa library's manifest states the licence of the whole library, which
get-propertyanswers:(get-property lib_string license)reads"Apache-2.0 AND BSD-2-Clause AND BSL-1.0 AND LGPL-2.0-or-later AND MIT". is-symbolanswers whether an atom is written as a symbol, whatever the engine holds for its name, beside upstream'sis-var,is-ground,is-exprandis-space.m.remove(defined)andm -= definedtake a Python definition out of its space whole, its equations, type declarations and@docin one transaction.seam:equation_changed/2tells an extension of every equation that enters or leaves a space, whichever door wrote it, once the outermost transaction around the write commits.metta_host_subtract/4is the host service a binding moves atoms through, so a moving door and the removing doors cannot disagree about which occurrence goes.
Fixed
- pymetta's Linux wheels carry
library(unicode), solib_unicodeloads from a wheel as it does from a checkout and under tsmetta. - A keyed catalog head keeps one row per key when writers race for it, so
Space.poolworkers declaring one algebra leave one row. Reported against 0.9.3 by a downstream integration. get-docwith a space, and soSpace.doc, answers every document the unaryget-docanswers, kinded and typed ones included. Reported against 0.9.3 by a downstream integration.unimport!of a package withdraws the package's own files, where it withdrew only the manifest's load.- A
definethat fails part way leaves the space as it was, and defining a function again after its equation was removed publishes it again. space -= atominsidespeculative()is discarded, and insideatomic()is one transaction, asspace.remove(atom)is.- Draining a space that holds a variable atom empties it, where it overflowed the stack.
- A second space declaring an algebra name leaves the first space's algebra computing with its own callables.
- Ctrl-C reaches every running evaluation, one that loops without returning from anything included, and a Ctrl-C after a query cursor no longer corrupts the process's memory.
(not-provable G)answers once for each binding of the variables of G the rest of the clause reads, where it answeredFalseonce per proof; every dual requires every answer of a sub-evaluation to fail; and an open one answers every value of its unbound variables as the negation at that value does, or refuses.- A compiled function continues past an
if,eliformatchthe way Python does, and a compiledmatchno longer multiplies its size with guarded cases and alternative patterns. - Six defects of SWI-Prolog's LibBF arithmetic, a rational zero raised to a negative power, and a LibBF host looping forever after
PL_cleanup()and a secondPL_initialise(), as the C seat'smt_close()andmt_open()do, are fixed by host patches, each with a one-goal reproduction. - On a native host built without GMP, as the Linux wheels' host now is, each seed draws its own sequence, in
with-seedand in every seeded draw of the libraries. SWI-Prolog's LibBF emulation gave every seed below 2^31 one sequence; a host patch seeds its Mersenne Twister as the reference implementation does, and a seed below 2^32 draws what the WebAssembly host always drew for it. - A read's inference count no longer depends on how many atoms the process made before it, and on the patched host a program's inference count no longer depends on when SWI-Prolog's gc thread collects.
- A garbage collection no longer calls into the engine.
Known issue
A method a class defines through @m.define inside a MeTTa() context stays visible after the context closes, so in a space created afterwards an expression headed by that method's name, such as (answer 4), evaluates to no answer rather than to itself. It is tracked for the next release.
The full list, with the development tooling's changes, is the 0.9.4 section of the changelog.
How this release was checked
These are the files built from the release commit and checked before upload. Every distribution builds an sdist and a pure wheel from an export of the files git tracks at that commit, and all 42 files pass twine check --strict. Each declares Apache-2.0 and carries PeTTa's MIT notice wherever PeTTa's code ships; metta-library-pack's names beside it the LGPL its plugins link. Each of pymetta's three manylinux wheels passes check_host_bundle.py, reads 22 of 22 platform capabilities with every library the shipped Prolog names loading, and boots from a clean virtual environment outside the repository, answering from its own host. The pure wheel beside PyPI's janus-swi on a stock SWI-Prolog is refused with the engine's own error naming the missing patches. All eighteen members install beside pymetta pinning pymetta==0.9.4: every entry point they declare loads, and metta-otel and metta-benchmarking, which declare none, import. Every published entry point resolves with each extra it declares: 525 of 525 combinations over Linux x86_64 and aarch64, macOS arm64 and x86_64, and Windows amd64, for CPython 3.12, 3.13 and 3.14.
pymetta 0.9.3
pymetta 0.9.3 and all seventeen integration packages at 0.9.3, each pinning pymetta==0.9.3. tsmetta 0.0.1-alpha.2 and cmetta 2.0.0 are released beside it on the same engine.
Before you upgrade
A SWI-Prolog you built yourself has to be rebuilt. A pymetta host now needs 37 patches to SWI-Prolog where 0.9.2's needed 19: the engine requires 35 to swipl-devel itself and pymetta the 2 to janus, and each refuses at boot a host that lacks one, naming each missing patch. On Linux x86_64 under CPython 3.12, 3.13 and 3.14 the wheel carries the patched host, so pip install pymetta is still the whole install; anywhere else, rebuild and redeclare the host as docs/patched-host.md describes. pymetta also checks its two janus patches right after boot and refuses a home that lacks one with EngineError.
The licence is Apache-2.0: pymetta and its seventeen members declared MIT through 0.9.2. The wheel carries LICENSE and NOTICE, and NOTICE keeps the MIT copyright and permission notice of PeTTa, the engine's origin.
These change what an existing program sees:
- Hook verdicts, typing-rule outcomes and translator-rule refusals are capitalized constructors. A pre-add or post-add handler answers
(Accept),(Accept <atom>),(Refuse <words>)or(Drop); anadd-typing-rule!outcome isAccept,(Refuse <reason>)orDefer. The lowercase spellings are refused by name. In Python the builders areAccept,RefuseandDrop, replacingaccept,refuseanddrop. - The Python seat's evaluation options
fuelandunmatchedare gone. Every evaluation runs in the evaluation fuel scope, so(pragma! max-stack-depth N)bounds anevalas it bounds a!form, and answers an unmatched call by the dispatch policy. - The
py-*doors refuse with the platform refusal naming the python capability wherever no Python seat is loaded, under tsmetta and in an engine booted without theextensionstoken, where each call used to answer itself unreduced. - A registration of Prolog that its contract refuses raises
RegistrationError, which is also aValueError, and carriesrequires, what the registration lacks.register_prolog(path=...)of a file that is not there raisesSourceNotFound. lib_process,lib_socketandlib_httpimport on a build without their capability, and only the call that needs it refuses, withPlatformCapabilityError.setup!names its subject by the ruleget-propertyandfromfollow: a bare name is a library under the library root and a relative path is written./x.- An open
get-atomsread of a native space meets the atoms shortest first, in insertion order within one length. MeTTa specifies no order across a space's atoms; a program that relied on the old order, SWI's procedure-table hash order, sees a different one. - A
|->lambda is named by its content,lambda_and 16 hex digits, instead of a process-wide counter, so a saved space names its lambdas the same in every process. - The engine no longer publishes
metta_typed_dispatch_applies/2.
New
- Every seat evaluates through one engine door,
metta_host_evaluate/5, which runs a term in the fuel scope and answers each value as data with its well-founded residue. - Registering Prolog as MeTTa functions is one engine service,
metta_register_prolog/3, which PyMeTTa'sregister_prolog, tsmetta'sregisterPrologand cmetta'smt_register_prologall cross once, so the three refuse the same names before a source loads. Space.copy()restores a space's rows into the copy the way a load restores a program and compiles in the copy exactly what the source had compiled (metta_host_copy_rows/2).- The library pack's page,
lib/README.md, shows a worked example of every one of the 61 libraries again, each quoted from the corpus program that exercises it and held to it by thelibrary-readmelane.
Fixed
- A Prolog source loaded on another thread, or inside an engine of its own, raises the errors it prints, where a syntax error in a source registered from a worker thread was lost.
- A term stored through a Python space provider comes back as the term the engine gave it, so a stored partial application applies as it does in a native space.
- A specialization that fails in one space no longer stops the same call specializing in another, writing a function into one space no longer compiles other spaces' waiting equations of that name, and importing a library no longer fails when an earlier import's function has been specialized in the same space.
- A library head backed by Prolog stays a function in every space still importing the library when the first space that imported it is dropped.
(get-property Subject Key)answers or refuses by name for every subject a program can write, and a library name of..is refused as leaving the library root.unicode-normalize'snfkc-casefoldremoves default-ignorable code points, as Unicode's NFKC_Casefold does.- lib_http's server answers a request whose Accept header carries a wildcard, as curl, browsers and Python's requests send.
@definecompiles a function that reuses a finishedforloop's variable as a later loop's counter.LimitSizeinlib_narsandlib_plnanswers at every size.- Two threads reading one platform capability for the first time no longer read an absent capability as present.
- No callback the garbage collector runs for the Python seat takes a lock, a
watch()iterator collected withoutclose()no longer cancels its subscription from inside the collector, and an abandonedChannelreleases its SWI message queue. CITATION.cffand the README's citation block name the release, where both read 0.8.0 through 0.9.2; a test now holds them, and the changelog's newest heading, to the version the package builds.
The full list, with the development tooling's changes, is the 0.9.3 section of the changelog.
Known issues
Each of these three is fixed after this release.
- A long-lived process that creates and releases many contexts and imports libraries into them grows the engine's predicate registry with its history, so a later import costs far more than the same import in a fresh process. One test run replayed in a single process held 7.7 million predicates, 1.55 million clauses and 4.4 GB by its 5,255th test, and a property test creating a fresh
MeTTa()and importinglib.filefor each example took about 130 s and added about 1.37 million predicates, against 4.8 s and under 500 in a fresh process. What amplifies the growth is still being identified. Each import's shadow repair also sweeps every import receipt the process holds, which costs time but does not add the predicates; a repair that reaches only the import's own names follows this release. - A race pulled through a streaming door, such as
race()or afncall, is counted partly inside that door's held engine, and that part varies with the schedule, so itsm.stats()block is not one integer: 200 races throughSpace.fnread 1,038 to 9,049 inferences, where the same race throughrunreads 1,167 every time. The fix takes a held goal's work through the engine's own accounting, so a stopped branch's discarded work stays out of the block. - Cancelling a spawned future costs the caller 27 inferences more when the task is still queued than when a carrier is already running it, and the schedule decides which, so two equal blocks can differ by 27. The fix charges every branch of the cancel alike.
How this release was checked
These are the files built from the release commit and checked before upload. Every distribution builds an sdist and a pure wheel from a tree holding only committed state, and all of them pass twine check --strict. Each of pymetta's three manylinux wheels passes check_host_bundle.py and boots from a clean virtual environment outside the repository under env -i, answering from its own host. The pure wheel beside PyPI's janus-swi on a stock SWI-Prolog is refused with EngineError. All seventeen members install beside pymetta pinning pymetta==0.9.3 and their entry points load. Every published entry point resolves with each extra it declares: 495 of 495 combinations over Linux x86_64 and aarch64, macOS arm64 and x86_64, and Windows amd64, for CPython 3.12, 3.13 and 3.14.
pymetta 0.9.2
pymetta 0.9.2 and all seventeen integration packages at 0.9.2. Nine of them are on PyPI for the first time: metta-otel, metta-pandas, metta-polars, metta-pyarrow, metta-pydantic, metta-remote, metta-sqlite, metta-tables and metta-websocket. So every extra pymetta declares now resolves.
- On Linux x86_64 under CPython 3.12, 3.13 and 3.14,
pip install pymettais the whole install: the wheel carries a patched SWI-Prolog asmetta/_host, with the janus bridge built against it. - The engine refuses to boot on a SWI-Prolog that does not carry every patch it relies on. It names each missing patch and points at docs/patched-host.md, instead of crashing or answering wrongly.
- The
engineextra no longer names the unpublishedpymetta-host, which madepip install "pymetta[engine]"unresolvable on Linux x86_64 in 0.9.1.
These are the 39 files built from commit 8096864 and verified before upload. Each manylinux wheel was installed into a clean virtual environment outside the repository and answered from its own host. The pure wheel, beside PyPI's janus-swi on a stock SWI-Prolog, was refused with metta's EngineError. Each of the nine new members was installed beside pymetta 0.9.2 and its entry point loaded.
After upload, every file on PyPI matches its verified build by SHA-256. Installing pymetta and a member from the index on CPython 3.12, 3.13 and 3.14 boots the engine from the bundled host. Every published entry point resolves with each extra it declares: 495 of 495 combinations over Linux x86_64 and aarch64, macOS arm64 and x86_64, and Windows amd64.