chemart / high-order-chem

View on GitHub

High-order chemistry (rules as molecules) — A chemistry whose rules are themselves molecules. The vessel holds two multisets, one of data and one of rules; each step draws a rule, counts its binding sites, draws that many data molecules, applies it, and returns ev…

chemistry official · built in core fidelity: book+decisions constructive #banzhaf-yamamoto

How it works

A chemistry whose rules are themselves molecules. The vessel holds two multisets, one of data and one of rules; each step draws a rule, counts its binding sites, draws that many data molecules, applies it, and returns everything - rule included - to the vessel. Because a rule is a molecule, its concentration matters: rules compete for substrate and can be added or removed at run time. The natural next step, rules that rewrite rules, is what the design points at without yet doing.

S
two multisets: data molecules (JSON integers or lists; species n<k> or compact JSON text such as [0,3,1,2]) and rule molecules (species rule:<name>, structure the rule text, e.g. divrule(m1, m2)) · implicit
R
rule + m1 + ... + mk -> rule + products(m1, ..., mk), k = binding sites of the rule; e.g. rule:divrule + n12 + n3 -> rule:divrule + n4 + n3 · arity variable - the number of binding sites of the drawn rule, plus the rule molecule itself
A
well-stirred-multiset · none - products replace educts; the data population changes only if a rule returns more or fewer molecules than it binds

Parameters

namedefaultmeaning
method
enum · structural
"soup" soup: the book's HighOrderChem.iterate loop, observed effective reactions with counts; closure: every reaction reachable from the distinct data molecules under deterministic rules (Chemart addition)
soup | closure
rules
dict · structural
{"divrule": 4} the rule multiset {rule: multiplicity}; this IS the chemistry. A rule is a built-in name (divrule, exchangeMachine, cutMachine, invertMachine, recombinationMachine), optionally written as its rule molecule 'divrule(m1, m2)', or an expression rule '[name:] x, y -> expr, ... [if condition]' (integers, + - * / % comparisons and or not, min max abs; see the module docstring). Rules are selected with probability proportional to multiplicity
NumberChemHO.py: {divrule: 4}; MolecularTSP.py: {exchangeMachine: 100, cutMachine: 100, invertMachine: 100, recombinationMachine: 1}
data
list · structural
[] explicit initial data multiset as a list of JSON integers or lists; overrides the random initialisation selected by init
init
enum · structural
"numbers" random data multiset when data is empty: numbers draws M integers uniformly from [minn, maxn] (NumberChemHO.py); tours draws M random tours on the ring of `cities` cities (MolecularTSP.py)
numbers | tours
M
int · population
100 number of data molecules drawn when data is empty
[1, 100000]; NumberChemHO.py popsize 100; MolecularTSP.py popsize 9
minn
int · structural
2 init numbers: lower bound of the uniform draw (inclusive)
[1, ∞]
maxn
int · structural
1000 init numbers: upper bound of the uniform draw (inclusive)
[1, ∞]
cities
int · structural
10 number of cities on the ring instance that the tour machines (and init tours) use; tours are permutations of 0..cities-1
[3, 1000]; MolecularTSP.py default 10
iterations
int · population
10000 soup only: number of iterations of the algorithm (one rule drawn per iteration, elastic and idle ones included)
[0, 1e+07]; NumberChemHO.run: 10000; MolecularTSP.py: up to 1000 generations of ceil(M * 100 / |rules|) iterations
max_species
int · structural
1000 closure only: budget of data species; status truncated when exceeded
[1, ∞]

High-order chemistry (rules as molecules)

One of the artificial chemistries of Banzhaf & Yamamoto, Artificial Chemistries (MIT Press, 2015), §appendix: Writing Your Own Artificial Chemistry in Python, 'A High-Order Chemistry' (figure 3, divrule); module list (HighOrderChem.py); first proposed by Yamamoto, 2014 (PyCellChemistry); Banzhaf & Yamamoto, 2015.

Its generator ships with the chemart library, so loading it runs no code from the hub and needs no trust_remote_code.

import chemart

net = chemart.generate_network("chemart/high-order-chem", seed=0)   # same as "high-order-chem"
print(net.summary())

What to look for

  • the divrule rule set reproduces the prime-number chemistry: primes are never consumed, composites are divided, and the prime fraction grows towards 1 (book appendix: 'the results should be the same as the original NumberChem implementation')
  • rule molecules are catalysts: every reaction has its rule on both sides and the rule multiset stays constant
  • rules compete for substrate: each iteration one rule is chosen with probability proportional to its multiplicity
  • changing the rule multiset changes the chemistry without changing the algorithm (e.g. the molecular TSP machines on tour molecules)

Implementation decisions

  • Security: the reference executes rule strings with exec. Chemart never executes a string: a rule is looked up by name in a registry of built-in functions ported from the reference examples (divrule from NumberChemHO.py and the book; exchangeMachine, cutMachine, invertMachine, recombinationMachine from MolecularTSP.py), or parsed and interpreted as an expression rule in a small documented language. Unknown names are rejected with ValueError. Subclass methods (the book's self.fold(m)) are replaced by the registry; a leading self. is accepted and ignored.
  • Binding sites: for named rules nsites is computed from the rule molecule exactly as figure 3 does (len(bsite.split(','))), and a user-written rule molecule must have as many sites as the function takes (the reference would crash on a mismatch). For expression rules nsites is the number of binding-site variables.
  • Multisets are lists; a random draw removes a uniformly chosen molecule, the same distribution as Multiset.expelrnd (each molecule with probability 1/total). The rule is drawn and reinjected every iteration, so the rule multiset never changes and rules are catalysts in the network (the rule species appears on both sides).
  • When fewer data molecules than binding sites are present, the rule is reinjected and nothing else happens (figure 3 lines 17 and 26); such iterations count towards `iterations` and are reported as extras.analysis.idle_draws.
  • divrule is written with Python 2 integer division in the book (m1 / m2 on ints); it is floor division (//), exact here because the quotient is only taken when it divides. Built-in rules collide elastically (return their educts) on molecules outside their domain (non-integers or 0 for divrule, non-tours for the machines), where the reference would raise.
  • Default rules {divrule: 4}, M = 100 from [2, 1000] and 10000 iterations are NumberChemHO.py's; the book only says 'at least one molecule of divrule(m1, m2)'. With a single rule type the multiplicity does not change the dynamics.
  • The observed network keeps the effective reactions (HighOrderChem.is_effective: product multiset differs from educt multiset) with firing counts; initial_state and extras.final_state include the rule molecules; extras.analysis records rule_draws per rule, and prime_fraction per generation of M iterations when the only rule is divrule on integers (NumberChemHO.nprimes).
  • Tour machines use MolecularTSP.py's ring topology (TSPgraph ring=True: gridsize 2N, radius N, fully meshed roads) and its fitness with penalties; a tour molecule is the list of cities rather than the string 'fitness [tour]', since the fitness is a function of the tour. The reference's %g rounding of the stored fitness is kept (the drawn molecules' fitness is compared after '%g' formatting, the new tour's exactly). The random topology is not offered: the dedicated molecular-tsp chemistry covers the full model. List molecules must be permutations of 0..cities-1 when a machine is in the rule set.
  • method closure is a Chemart addition (chemart.expand.expand, ordered reactants, each rule producing its own reaction); it is only defined for deterministic rules, so the stochastic tour machines are rejected.
  • The book's caveat is kept: this is not yet truly high-order, since rules never take rules as educts and cannot rewrite rules (no typed binding sites).
  • v1 parameters rules (callable) and seed (matrix) are replaced by the JSON rule multiset `rules` and the data multiset `data`/`init`; the v1 name `seed` is the generate_network seed.

Sources

  • Banzhaf, W. & Yamamoto, L. (2015). Artificial Chemistries. MIT Press. Appendix, 'A High-Order Chemistry' (figure 3 and the divrule listing).
  • PyCellChemistry src/HighOrderChem.py, src/NumberChemHO.py, src/MolecularTSP.py and src/artchem/Multiset.py (Yamamoto, 2014). https://github.com/laryamamoto/PyCellChemistry

Banzhaf & Yamamoto, Artificial Chemistries (2015), §appendix: Writing Your Own Artificial Chemistry in Python, 'A High-Order Chemistry' (figure 3, divrule); module list (HighOrderChem.py)

Preview at default parameters

198 species · 154 reactions · status observed · seed 1

rule:divrule + n261 + n87 -> rule:divrule + n3 + n87  (x1)
rule:divrule + n771 + n3 -> rule:divrule + n257 + n3  (x1)
rule:divrule + n981 + n3 -> rule:divrule + n327 + n3  (x1)
rule:divrule + n410 + n820 -> rule:divrule + n410 + n2  (x1)
rule:divrule + n2 + n866 -> rule:divrule + n2 + n433  (x1)
rule:divrule + n369 + n3 -> rule:divrule + n123 + n3  (x1)
rule:divrule + n512 + n64 -> rule:divrule + n8 + n64  (x1)
rule:divrule + n918 + n2 -> rule:divrule + n459 + n2  (x1)
rule:divrule + n2 + n774 -> rule:divrule + n2 + n387  (x1)
rule:divrule + n327 + n981 -> rule:divrule + n327 + n3  (x1)
rule:divrule + n3 + n750 -> rule:divrule + n3 + n250  (x1)
rule:divrule + n123 + n3 -> rule:divrule + n41 + n3  (x1)
rule:divrule + n3 + n789 -> rule:divrule + n3 + n263  (x1)
rule:divrule + n126 + n2 -> rule:divrule + n63 + n2  (x1)
rule:divrule + n818 + n2 -> rule:divrule + n409 + n2  (x1)
rule:divrule + n41 + n205 -> rule:divrule + n41 + n5  (x1)
rule:divrule + n3 + n135 -> rule:divrule + n3 + n45  (x1)
rule:divrule + n3 + n546 -> rule:divrule + n3 + n182  (x1)
rule:divrule + n542 + n2 -> rule:divrule + n271 + n2  (x1)
rule:divrule + n2 + n474 -> rule:divrule + n2 + n237  (x1)
rule:divrule + n805 + n5 -> rule:divrule + n161 + n5  (x1)
rule:divrule + n3 + n216 -> rule:divrule + n3 + n72  (x1)
rule:divrule + n486 + n2 -> rule:divrule + n243 + n2  (x1)
rule:divrule + n594 + n3 -> rule:divrule + n198 + n3  (x1)
rule:divrule + n410 + n41 -> rule:divrule + n10 + n41  (x1)
rule:divrule + n582 + n2 -> rule:divrule + n291 + n2  (x1)
rule:divrule + n87 + n3 -> rule:divrule + n29 + n3  (x1)
rule:divrule + n72 + n3 -> rule:divrule + n24 + n3  (x1)
rule:divrule + n970 + n5 -> rule:divrule + n194 + n5  (x1)
rule:divrule + n29 + n754 -> rule:divrule + n29 + n26  (x2)
rule:divrule + n282 + n2 -> rule:divrule + n141 + n2  (x1)
rule:divrule + n8 + n824 -> rule:divrule + n8 + n103  (x1)
rule:divrule + n384 + n3 -> rule:divrule + n128 + n3  (x1)
rule:divrule + n3 + n513 -> rule:divrule + n3 + n171  (x1)
rule:divrule + n624 + n26 -> rule:divrule + n24 + n26  (x1)
rule:divrule + n828 + n2 -> rule:divrule + n414 + n2  (x1)
rule:divrule + n2 + n8 -> rule:divrule + n2 + n4  (x5)
rule:divrule + n29 + n551 -> rule:divrule + n29 + n19  (x1)
rule:divrule + n3 + n237 -> rule:divrule + n3 + n79  (x1)
rule:divrule + n3 + n387 -> rule:divrule + n3 + n129  (x1)
… 114 more

Generated by the uploader with chemart push; the hub never runs uploaded code.