Skip to content

Releases: MesTTo/MeTTa

pymetta 0.9.4

Choose a tag to compare

@MesTTo MesTTo released this 30 Sep 06:36

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 it space.remove, space -= atom and transfer, takes an occurrence equal to the atom before an older one that only unifies with it, so s += a; s -= a leaves the space it found, and it no longer binds the caller's pattern. transfer lands the atom that left, as the source stored it.
  • A removal event from remove, -=, transfer and subtract-atom carries 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 of map-atom, filter-atom and foldl-atom answer what their binder spellings answer.
  • define inside its own space's with m.batch(): block is refused, as remove, transfer, clear and declare there 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.algebra takes a Python callable for every operation role, and an operation made from a callable is named for the space that declared it.
  • integrate.module_ops maps an implicit name's underscores to hyphens, as op does, so a module's add_one registers add-one.
  • markup-parse-html reads 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's library/semweb/eor.rdfs, because no terms permitting their redistribution were found; load_html/3 parses HTML5 once lib_markup is loaded.
  • lib_soft's soft-symbol? is gone: call is-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-NOTICES beside 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-property answers: (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-symbol answers whether an atom is written as a symbol, whatever the engine holds for its name, beside upstream's is-var, is-ground, is-expr and is-space.
  • m.remove(defined) and m -= defined take a Python definition out of its space whole, its equations, type declarations and @doc in one transaction.
  • seam:equation_changed/2 tells 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/4 is 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), so lib_unicode loads 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.pool workers declaring one algebra leave one row. Reported against 0.9.3 by a downstream integration.
  • get-doc with a space, and so Space.doc, answers every document the unary get-doc answers, 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 define that fails part way leaves the space as it was, and defining a function again after its equation was removed publishes it again.
  • space -= atom inside speculative() is discarded, and inside atomic() is one transaction, as space.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 answered False once 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, elif or match the way Python does, and a compiled match no 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 second PL_initialise(), as the C seat's mt_close() and mt_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-seed and 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

Choose a tag to compare

@MesTTo MesTTo released this 25 Sep 19:12

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); an add-typing-rule! outcome is Accept, (Refuse <reason>) or Defer. The lowercase spellings are refused by name. In Python the builders are Accept, Refuse and Drop, replacing accept, refuse and drop.
  • The Python seat's evaluation options fuel and unmatched are gone. Every evaluation runs in the evaluation fuel scope, so (pragma! max-stack-depth N) bounds an eval as 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 the extensions token, where each call used to answer itself unreduced.
  • A registration of Prolog that its contract refuses raises RegistrationError, which is also a ValueError, and carries requires, what the registration lacks. register_prolog(path=...) of a file that is not there raises SourceNotFound.
  • lib_process, lib_socket and lib_http import on a build without their capability, and only the call that needs it refuses, with PlatformCapabilityError.
  • setup! names its subject by the rule get-property and from follow: a bare name is a library under the library root and a relative path is written ./x.
  • An open get-atoms read 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's register_prolog, tsmetta's registerProlog and cmetta's mt_register_prolog all 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 the library-readme lane.

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's nfkc-casefold removes 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.
  • @define compiles a function that reuses a finished for loop's variable as a later loop's counter.
  • LimitSize in lib_nars and lib_pln answers 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 without close() no longer cancels its subscription from inside the collector, and an abandoned Channel releases its SWI message queue.
  • CITATION.cff and 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 importing lib.file for 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 a fn call, is counted partly inside that door's held engine, and that part varies with the schedule, so its m.stats() block is not one integer: 200 races through Space.fn read 1,038 to 9,049 inferences, where the same race through run reads 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

Choose a tag to compare

@MesTTo MesTTo released this 23 Sep 14:46

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 pymetta is the whole install: the wheel carries a patched SWI-Prolog as metta/_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 engine extra no longer names the unpublished pymetta-host, which made pip 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.