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.3.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.3-py3-none-any.whl (1.5 MB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: pygx-0.5.3.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.3.tar.gz
Algorithm Hash digest
SHA256 a32aa7a33c7d78395419c06810b67f409fa2e3d8f44da8f85028b7689f6a1822
MD5 df32fbddefb507e59f016e387924d95c
BLAKE2b-256 94fb75b9ada47687c647e560d3447d1ca3d8df12ddb126bdc6f7b294589c6f6b

See more details on using hashes here.

Provenance

The following attestation bundles were made for pygx-0.5.3.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.3-py3-none-any.whl.

File metadata

  • Download URL: pygx-0.5.3-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.3-py3-none-any.whl
Algorithm Hash digest
SHA256 5bd2af490d9967fb73f2e7fb4c2fa1d46a174abd45ff89a30118a0f4a6bdf350
MD5 3fa27a3349399de5506138e61dfd638e
BLAKE2b-256 7d590d8764ef53cae91cef86d3167d32d707cb7eb3bb5b735c322b26bea277f0

See more details on using hashes here.

Provenance

The following attestation bundles were made for pygx-0.5.3-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

0.5.5

2 files

0.5.4

2 files

This release

0.5.3 This release

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