Skip to main content


Meta-program Python Objects

Website · Documentation · Bird's-eye view · Tutorials · API

PyPI Python License

Ordinary objects are built and then sealed — the call that produced them is gone. A pg.Object behaves like any Python object and keeps the structure it was built from — so your code can generate, inspect, diff, patch and tune it as easily as it runs it.

import pygx as pg

class Model(pg.Object):
    units: int = 8

class Trainer(pg.Object):
    model: Model
    lr: float = 0.01

t = Trainer(model=Model(units=128))
t.sym_init_args                     # {'model': Model(units=128), 'lr': 0.01}

# Edit it anywhere in the tree — validated, and everything it touched re-initializes
t.sym_rebind({'model.units': 256})

# Replace any value with a space, and the class IS the search space
space = Trainer(model=Model(units=pg.oneof([64, 128])), lr=pg.oneof([0.1, 0.01]))
list(pg.iter(space))                # 4 programs: (64, 0.1) (64, 0.01) (128, 0.1) (128, 0.01)

No separate schema to keep in sync, no loop to rewrite when a parameter is added, and no path-walking getattr/setattr to apply an override. That one difference is the whole library — everything else is a consequence of it. The idea has a name: symbolic programming, a paradigm where a program can manipulate its own components as if they were plain data.

Install

pip install pygx                # Python 3.12+
pip install "pygx[io]"          # fsspec-backed remote IO (GCS, S3, ...)
pip install "pygx[concurrent]"  # parallel execute/map with retries + progress

More

Programs written by algorithms — hand the space to a search
algo = pg.algo.evolution.regularized_evolution(
    pg.algo.evolution.mutators.Uniform())

for net, feedback in pg.iter(space, 30, algo):
    feedback(score(net))     # your objective; the algorithm does the rest

Each net is a real, validated instance of your class — not a parameter vector you have to decode. The algorithm mutates the representation, so the same Uniform() mutator works on a neural architecture, a tour of cities, or a symbolic expression, without being written for any of them.

Worked examples: OneMax · Traveling Salesperson · Function Regression

Two programs, structurally comparedpg.diff
pg.diff(baseline, candidate)
# Trainer(model=Model(units=Diff(left=8, right=16)))

Not a text diff of two dumps — a structural one that answers which knob differs, at the position it differs. When runs are configs, this is the question you actually ask.

Programs that aren't finished yet — holes with names, not None
p = Trainer.partial()    # `model` has no default — so it's a hole
pg.is_partial(p)         # True
p.sym_missing()          # {'model': MISSING_VALUE}

An abstract object's __init__ doesn't even run until it becomes concrete — so a program can be assembled in stages by a user, a config file, a search, or a model, and still be a real, checkable object at every stage.

Values that resolve from where they sit — no threading through constructors
class Layer(pg.Object, topo=True):
    dropout: Any = pg.symbolic.ValueFromParentChain()

Net(dropout=0.5, layer=Layer()).layer.dropout    # 0.5 — read from the ancestor

The alternative is threading the argument through every constructor in between, or a global. Here the field is optional at construction and resolved at read time from its position in the tree.

Code you don't ownpg.symbolize and pg.detour
Sym = pg.symbolize(ThirdPartyClass)     # make it symbolic, no source edits

with pg.detour([(Adam, LAMB)]):         # change what a LIBRARY constructs
    third_party_training_loop()         # → returns a LAMB

detour redirects construction inside code you cannot edit — nested, transitive, outer-scope-wins. The usual answers are monkeypatching or forking.

Provenance for generated programs — where did this one come from?
with pg.track_origin():
    variant = base.sym_clone()

variant.sym_origin.source is base       # True
variant.sym_origin.tag                  # 'clone'

When programs are produced by other programs — mutated, evolved, sampled — that bookkeeping is otherwise yours to build.

Seeing itpg.to_html renders any value as a browsable tree
pg.to_html(trainer)      # an interactive, collapsible tree

A deeply nested program is unreadable as repr output. Any symbolic value renders as a browsable tree, in a notebook or a file.

See it all on www.pygx.com — every capability with the problem it solves, side by side with the plain-Python alternative.

Fast. Hot paths run in a native Rust core (pygx-core, installed automatically); the pure-Python implementation remains the executable specification, and the full suite runs against both cores on every PR. Validated construction beats pydantic v2 with the whole symbolic model attached, and attribute reads are at dataclass parity — see the full report.

Portable. Wheels for Linux, macOS and Windows on CPython 3.12–3.14, plus a genuinely free-threaded 3.14t wheel (the GIL stays off; see docs/design/gil-free.md §3). Elsewhere PyGX falls back to the pure-Python core with identical behavior.

And when not to reach for it. If your objects are only ever built and read — request payloads, plain records, a config loaded once and never manipulated — a dataclass or pydantic model is the better tool. PyGX earns its keep the moment a program becomes something you operate on.

Documentation

Bird's-eye view 5–10 minute tour of the core ideas
Tutorials tracks for Python, ML, AutoML, and Evolution
Learning PyGX Symbolic OOP and Symbolic Detour, conceptually
API Reference generated from source
Style guide authoring pg.Object subclasses

Runnable notebooks live in docs/notebooks/, source examples in examples/.

Upgrading? See the 0.5 migration guide. 0.5.2 removed three long-misspelled names without aliases; earlier versions are covered by the 0.4 guide.

Background

PyGX was originally built at Google Brain / DeepMind by Daiyi Peng to power automated machine learning, under the name PyGlove. The abstraction underneath — symbolic object-oriented programming — turned out to be far more general than AutoML. The original paper was published at NeurIPS 2020, and the same ideas drive Google Cloud Vertex AI NAS, Pax, and Vizier.

Citing PyGX
@inproceedings{peng2020pyglove,
  title={PyGlove: Symbolic programming for automated machine learning},
  author={Peng, Daiyi and Dong, Xuanyi and Real, Esteban and Tan, Mingxing and Lu, Yifeng and Bender, Gabriel and Liu, Hanxiao and Kraft, Adam and Liang, Chen and Le, Quoc},
  booktitle={Advances in Neural Information Processing Systems (NeurIPS)},
  volume={33},
  pages={96--108},
  year={2020}
}

License

Apache License 2.0. PyGX is derived from PyGlove (also Apache 2.0); see LICENSE and the per-file copyright headers for attribution.

PyGX is developed by Daiyi Peng.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pygx-0.5.5.tar.gz (1.3 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pygx-0.5.5-py3-none-any.whl (1.5 MB view details)

Uploaded Python 3

File details

Details for the file pygx-0.5.5.tar.gz.

File metadata

  • Download URL: pygx-0.5.5.tar.gz
  • Upload date:
  • Size: 1.3 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for pygx-0.5.5.tar.gz
Algorithm Hash digest
SHA256 4bfc35fafd77503c99c2ea94934f65733b38b813540139c50fc7a397d0a0b71a
MD5 ad816685c20d83990c5ec8e3582c0883
BLAKE2b-256 e44651ab1599e1d09a925e0d5f996f4e4c14d8ade14eede94e06fc0737e93587

See more details on using hashes here.

Provenance

The following attestation bundles were made for pygx-0.5.5.tar.gz:

Publisher: pypi.yaml on free-solo/pygx

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pygx-0.5.5-py3-none-any.whl.

File metadata

  • Download URL: pygx-0.5.5-py3-none-any.whl
  • Upload date:
  • Size: 1.5 MB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for pygx-0.5.5-py3-none-any.whl
Algorithm Hash digest
SHA256 52a1b034d7a735ddd24f79dbef35c58fe51e728871357258f6feac0309784139
MD5 49ad20b9d762689aab2d49aad8ceb88c
BLAKE2b-256 c4b37ed940b7eb72159bf8a0678d917d81f08d3a43704eca8cc8165ef7f87b93

See more details on using hashes here.

Provenance

The following attestation bundles were made for pygx-0.5.5-py3-none-any.whl:

Publisher: pypi.yaml on free-solo/pygx

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.5.5 This release

2 files

0.5.4

2 files

0.5.3

2 files

0.5.2

2 files

0.5.1

2 files

0.5.0

2 files

0.4.8

2 files

0.4.7

2 files

0.4.6

2 files

0.4.5

2 files

0.4.4

2 files

0.4.3

2 files

0.4.2

2 files

0.4.1

2 files

0.4.0

2 files

0.3.4

2 files

0.3.2

2 files

0.3.1

2 files

0.3.0

2 files

0.2.3

2 files

0.2.2

2 files

0.2.1

2 files

0.2.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page