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.06566594173588765, 0.08901794715652576, 0.23549250176892222, 0.29066539597222857, 0.03538636695893471, 0.021432408997347108, 0.06830599300293316, 0.07412138402999124, 0.021497327645020436, 0.09841473273220913 ], "P1": [ 0.04956041578170093, 0.052482099599744474, 0.16902010720434282, 0.06089639580981915, 0.12238852929715743, 0.052101213633520105, 0.2224519701542751, 0.013644502720908558, 0.11585661283950999, 0.14159815295902145 ], "P2": [ 0.004132308655693356, 0.32804681557541054, 0.034186836296353496, 0.01670458868887259, 0.2148933374915759, 0.13736848481534147, 0.023250352497643734, 0.0604824166875394, 0.05557944492879496, 0.12535541436277453 ], "P3": [ 0.07497180006894322, 0.3533814901330103, 0.02646136631486612, 0.03616975663656391, 0.015781883892374837, 0.05148092206412269, 0.1696559152784467, 0.05722052025284963, 0.11232945089497481, 0.10254689446384779 ], "SUM": { "kind": "aggregator", "name": "sum" } }, "properties": { "software": "aGrUM 3.2.0", "creation": "2026-09-28 15:47:45.972", "lastModification": "2026-09-28 15:47:45.986" }}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"}
