Aggregators
![]() | ![]() |
Aggregators are special type of nodes that includes a generic CPT for any numbers of parents.
pyAgrum proposes a list of such aggregators. Some of then are used below.
import pyagrum as gumimport pyagrum.lib.notebook as gnbmin_x = 0max_x = 15
bn = gum.BayesNet()l = [bn.add(gum.RangeVariable(item, item, min_x, max_x)) for item in ["a", "b", "c", "d", "e", "f"]]
gum.config["notebook", "histogram_line_threshold"] = 15nmax = bn.addMAX(gum.RangeVariable("MAX", "MAX", min_x, max_x))bn.addArc(l[0], nmax)bn.addArc(l[1], nmax)bn.addArc(l[2], nmax)nmin = bn.addMIN(gum.RangeVariable("MIN", "MIN", min_x, max_x))bn.addArc(l[3], nmin)bn.addArc(l[4], nmin)bn.addArc(l[5], nmin)nampl = bn.addAMPLITUDE(gum.RangeVariable("DELTA", "DELTA", 0, max_x - min_x))bn.addArc(nmax, nampl)bn.addArc(nmin, nampl)nmedian = bn.addMEDIAN(gum.RangeVariable("MEDIAN", "MEDIAN", min_x, max_x))for n in [l[0], l[1], l[2], l[3]]: bn.addArc(n, nmedian)## potential for median has a size : 16^5=2^20 double !nexists = bn.addEXISTS(gum.LabelizedVariable("EXISTS_0", "EXISTS"), 0)bn.addArc(l[0], nexists)bn.addArc(l[1], nexists)bn.addArc(l[2], nexists)nforall = bn.addFORALL(gum.LabelizedVariable("FORALL_1", "FORALL"), 1)bn.addArc(l[3], nforall)bn.addArc(l[4], nforall)bn.addArc(l[5], nforall)ncount = bn.addCOUNT(gum.RangeVariable("COUNT_1", "COUNT_1,", 0, 3), 1)bn.addArc(l[0], ncount)bn.addArc(l[1], ncount)bn.addArc(l[2], ncount)for nod in l: bn.cpt(nod).fillWith(1).normalize()gnb.showInference(bn, size="13")## dot | neato | fdp | sfdp | twopi | circo | osage | patchworkgum.config["notebook", "graph_rankdir"] = "LR"gnb.showInference(bn, size="13", evs={"MEDIAN": [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0]})gum.config.reset()## if the roots do not have uniform but random distributionfor nod in l: bn.generateCPT(nod)
gnb.showInference(bn, size="13")gnb.showInference(bn, size="13", evs={"MEDIAN": [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0]})Input/Output
Section titled “Input/Output”Aggregator (and ICI model) nodes have no stored CPT — the probability is computed on the fly from the aggregator’s rule. Saving to most BN file formats (BIF, BIFXML, DSL, XDSL, net, UAI, O3PRM) flattens that computed table into a full dense CPT before writing it out. Round-tripping through one of these formats therefore loses the aggregator: reloading the file gives back a plain node with a dense CPT, not an aggregator (bad round trip).
Only aGrUM’s native jgum (JSON) and bgum (binary) formats preserve the aggregator/ICI type exactly. Instead of a flat array of probabilities, the cpt entry for such a node is a small object describing its type and parameters, e.g. {"kind": "aggregator", "name": "forall", "value": 1} for a FORALL node, or {"kind": "ici", "name": "MultiDimNoisyORCompound", ...} for a Noisy-Or node.
import jsonimport osimport tempfile
demo = gum.BayesNet()ps = [demo.add(gum.RangeVariable(f"P{i}", "", 0, 9)) for i in range(4)]for i in range(4): demo.cpt(f"P{i}").randomCPT()nsum = demo.addSUM(gum.RangeVariable("SUM", "", 0, 9))for p in ps: demo.addArc(p, nsum)
with tempfile.TemporaryDirectory() as d: bif_path = os.path.join(d, "demo.bif") jgum_path = os.path.join(d, "demo.jgum") gum.saveBN(demo, bif_path) gum.saveBN(demo, jgum_path) bif_size = os.path.getsize(bif_path) jgum_size = os.path.getsize(jgum_path) with open(jgum_path) as f: jgum_content = json.load(f)
print(f"BIF (dense CPT) : {bif_size:>8} bytes")print(f"jgum (compact aggregator): {jgum_size:>8} bytes")print()print(json.dumps(jgum_content, indent=2))BIF (dense CPT) : 371370 bytesjgum (compact aggregator): 1167 bytes
{ "type": "BN", "GumJsonVersion": "1.0", "nodes": [ "P0[10]", "P1[10]", "P2[10]", "P3[10]", "SUM[10]" ], "parents": { "P0": [], "P1": [], "P2": [], "P3": [], "SUM": [ "P0", "P1", "P2", "P3" ] }, "cpt": { "P0": [ 0.061157582198104944, 0.11473354354843804, 0.14721866971494024, 0.015710760875219387, 0.07902679683615449, 0.2201336891194507, 0.19960712309126405, 0.13457420972076284, 0.02449667194468319, 0.0033409529509821345 ], "P1": [ 0.16119287861919768, 0.3247543151034207, 0.06382763236768846, 0.04310274441563966, 0.13757716962231947, 0.05472563034833722, 0.060414807265596226, 0.01952163493198389, 0.019853423571866036, 0.11502976375395069 ], "P2": [ 0.020056335980372576, 0.1631652896836549, 0.08058424198549799, 0.08034835521538869, 0.02701261173974734, 0.31361143833079475, 0.13288297420572137, 0.05657029654561896, 0.09647346035832038, 0.029294995954883052 ], "P3": [ 0.0038946757776496845, 0.031536825664451734, 0.1859583652526521, 0.0316389333101576, 0.01885131028863113, 0.2133602375196209, 0.09835110784338497, 0.12785690902887914, 0.24167572375436053, 0.0468759115602122 ], "SUM": { "kind": "aggregator", "name": "sum" } }, "properties": { "software": "aGrUM 3.2.1", "creation": "2026-10-09 06:52:42.734", "lastModification": "2026-10-09 06:52:42.757" }}Indeed, the cpt for the node SUM should have a size of floats. Instead :
print(json.dumps(jgum_content["cpt"]["SUM"], indent=2)){ "kind": "aggregator", "name": "sum"}
