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) : 371379 bytesjgum (compact aggregator): 1176 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.041534047697483666, 0.30354711427410097, 0.013285607869804095, 0.04649931056197004, 0.1916200355854209, 0.2294082863043041, 0.003181428251623264, 0.012228270478849446, 0.13747521464730572, 0.021220684329137818 ], "P1": [ 0.007526694046102483, 0.14764054419103842, 0.051923935684058564, 0.13298046897004068, 0.08585622728872372, 0.10119553889644778, 0.01803484437163927, 0.13493739063328558, 0.10917532904586857, 0.21072902687279493 ], "P2": [ 0.0033006997002650758, 0.15596241228190494, 0.04311938547689714, 0.02371039329531166, 0.14004542721348606, 0.26035599870667114, 0.004945723144842429, 0.12610728825832662, 0.031124541490831104, 0.2113281304314638 ], "P3": [ 0.025519186146852467, 0.3146990536221461, 0.31186305212214427, 0.0004965699225364384, 0.12762955717599866, 0.11211823440944402, 0.019636778605034655, 0.021011839114818298, 0.03889227733480638, 0.028133451546218713 ], "SUM": { "kind": "aggregator", "name": "sum" } }, "properties": { "software": "aGrUM 3.1.0", "creation": "2026-08-18 10:00:21.010", "lastModification": "2026-08-18 10:00:21.027" }}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"}
