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

Uploaded Python 3

File details

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

File metadata

  • Download URL: pygx-0.5.4.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.4.tar.gz
Algorithm Hash digest
SHA256 b615fa56e0faefbdea84f98c2db90d40d583226b0a123a0f5e83d09ace2f8847
MD5 be5bee6e51dfc91a7e63e386c4fa3252
BLAKE2b-256 7fcdb7da3afd77a6c910f79a6de372e10e077cf69668976c10ea71604f3d75c8

See more details on using hashes here.

Provenance

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

File metadata

  • Download URL: pygx-0.5.4-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.4-py3-none-any.whl
Algorithm Hash digest
SHA256 708a44ef390b5ff6e24b69ce9a0ffdbd9991c27d2150d60a1907c147814e5ca7
MD5 785816e961c884f40a67863344ad27be
BLAKE2b-256 c140505ffe5699f7ed2d73bbaf8743dd5ce84a9fde668dd9424596fc89c04d4f

See more details on using hashes here.

Provenance

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

This release

0.5.4 This release

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