{"id":17970418,"url":"https://github.com/andreacasalino/easy-factor-graph","last_synced_at":"2025-10-04T07:53:50.076Z","repository":{"id":37026106,"uuid":"184729632","full_name":"andreacasalino/Easy-Factor-Graph","owner":"andreacasalino","description":"General purpose C++ library for managing discrete factor graphs","archived":false,"fork":false,"pushed_at":"2024-02-08T17:57:34.000Z","size":18808,"stargazers_count":36,"open_issues_count":1,"forks_count":4,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-16T01:45:59.007Z","etag":null,"topics":["artificial-intelligence","artificial-intelligence-algorithms","bayesian-methods","bayesian-network","cpp","factor-graph","factor-graphs","graphical-models","machine-learning","probabilistic-graphical-models","probability","training"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/andreacasalino.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2019-05-03T09:24:08.000Z","updated_at":"2025-02-01T18:56:13.000Z","dependencies_parsed_at":"2023-12-05T00:26:17.709Z","dependency_job_id":"4896be85-ffa4-424e-bd1d-73164289aa20","html_url":"https://github.com/andreacasalino/Easy-Factor-Graph","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andreacasalino%2FEasy-Factor-Graph","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andreacasalino%2FEasy-Factor-Graph/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andreacasalino%2FEasy-Factor-Graph/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andreacasalino%2FEasy-Factor-Graph/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/andreacasalino","download_url":"https://codeload.github.com/andreacasalino/Easy-Factor-Graph/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245453899,"owners_count":20617938,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["artificial-intelligence","artificial-intelligence-algorithms","bayesian-methods","bayesian-network","cpp","factor-graph","factor-graphs","graphical-models","machine-learning","probabilistic-graphical-models","probability","training"],"created_at":"2024-10-29T15:05:14.964Z","updated_at":"2025-10-04T07:53:45.057Z","avatar_url":"https://github.com/andreacasalino.png","language":"C++","readme":"![binaries_compilation](https://github.com/andreacasalino/Easy-Factor-Graph/actions/workflows/runTests.yml/badge.svg)\n\n- [What is EFG](#intro)\n- [Project structure](#contents)\n- [Samples](#samples)\n- [Python](#python)\n- [CMake support](#cmake-support)\n- [Usage](#usage)\n- [EFG GUI](#efg-gui)\n\n## INTRO\n\n**Easy Factor Graph**, aka **EFG**, is a general purpose c++ library for handling **undirected graphical models**, sometimes called also **factor graphs**.\n**Undirected graphical models** are probabilistic models similar to **bayesian networks**, but offerring some nicer \nproperties. Not familiar with this kind of concepts? Don't worry, have a look at the [documentation](https://github.com/andreacasalino/Easy-Factor-Graph/blob/master/doc/EFG.pdf) in the **doc** folder before diving into the code ;).\n**Random Fields** as well as **Conditional Random Fields** are particular classes of **undirected graphical models** and can be easily created and **trained** using this library.\n\nRememebr to leave a **star** if you have found this repository useful.\n\n![Undirect models](./img/img2.png)\n\nTraining can be done using the gradient-base approaches implemented of [this](https://github.com/andreacasalino/TrainingTools) external library.\n\nIn particular, **EFG** is able to:\n * dynamically build and update undirected factor graph, inserting one by one the factors that compose the model\n * dynamically set the group of evidences\n * perform belief propagation on both **loopy graph** and **polytree** like structure in order to\n   * get the **marginal conditioned distribution** of an hidden variable w.r.t. the current evidence set\n   * get the **maximum a posteriori** of an hidden variable (or for the entire hidden set in one single call) w.r.t. the current evidence set\n * import or export models from and to xml file\n * import or export models from and to json string (or file)\n * draw samples for the variables composing the model\n * train **random** and **conditional random fields** with gradient based approaches (**gradient descend**, **conjugate gradient descend**, **quasi newton method**, etc.)\n\nWith respect to similar libraries, **EFG** is also able to:\n * enforce the fact that group of tunable factors should have the same weight\n * exploits an internal thread pool in order to dramatically reduce the time required to:\n   * perform belief propagation\n   * train **random** and **conditional random fields**\n   * draw samples for the variables involved in the model\n * **python** bindings of this library are offered by [this repo](https://github.com/andreacasalino/Easy-Factor-Graph-py), which is actually a [**python** package](https://pypi.org/project/efg/) that can be **pip** installed.\n\n## CONTENTS\n\nHaven't yet left a **star**? Do it now! :).\n\nThis project is structured as follows:\n * the documentation in ./doc explains both how to use **EFG** as well give some theoretical background about **undirected graphical models**\n * the sources of the **EFG** library are contained in ./src\n * ./samples contains 8 classes of examples, extensively showing how to use **EFG**\n\n\n## SAMPLES\n\nHaven't yet left a **star**? Do it now! :).\n\nThe samples contained in the [samples](./samples) folder and extensively shows how to use **EFG**.\nAll of the samples consume a library of utilities called **Samples-Helpers**, which contain common functionalities like printing utilities, that are not part (and don't need to be) of **EFG**.\n\n## PYTHON\n\nWait a minute ... **Easy Factor Graph** is great, but I don't know **C++** and I am more used to **python** ... well [this package](https://pypi.org/project/efg/) is a **pip** installable wrapper of **EFG** created with [pybind](https://github.com/pybind/pybind11). Combine the power of **EFG** and **python** in your next project!\n\n## CMAKE SUPPORT\n\nHaven't yet left a **star**? Do it now! :).\n\nTo consume this library you can rely on [CMake](https://cmake.org).\nMore precisely, You can fetch this package and link to the **EFG** library:\n```cmake\ninclude(FetchContent)\nFetchContent_Declare(\nefg\nGIT_REPOSITORY https://github.com/andreacasalino/Easy-Factor-Graph\nGIT_TAG        master\n)\nFetchContent_MakeAvailable(efg)\n```\n\nand then link to the **EFG** library:\n```cmake\ntarget_link_libraries(${THE NAME OF THE TARGET NEEDING EFG}\n   EFG-Core\n)\n```\n### TRAINING CAPABILITIES\n\nThe possibility to train a model is enabled by deafult. However, such functionality rely on [this](https://github.com/andreacasalino/TrainingTools) external library, which might slow down the time required to set up the cmake project.\nTherefore, if you don't need that you can set the CMake option **BUILD_EFG_TRAINER_TOOLS** equal to **OFF**.\nHowever, after disabling that option you will still able to get the tunable weights of a model, as well as their gradient, allowing you to use or implement another gradient based trainer. \n\nThe external package for performing training uses [**Eigen**](https://gitlab.com/libeigen/eigen) as internal linear algebra engine. \n**Eigen** is by default [fetched](https://cmake.org/cmake/help/latest/module/FetchContent.html) from the official gitlab repo by **CMake** and made available.\nHowever, if you already have installed **Eigen** on your machine you can also decide to use that local version, by [setting](https://www.youtube.com/watch?v=LxHV-KNEG3k\u0026t=1s) the **CMake** option **EIGEN_INSTALL_FOLDER** equal to the root folder storing the local **Eigen** you want to use.\n\n### XML SUPPORT\n\nBy default, the features to export and import models from **XML** files are enabled. If you don't need them, put the CMake option **BUILD_EFG_XML_CONVERTER** to **OFF**.\n\n### JSON SUPPORT\n\nBy default, the features to export and import models from **JSON** files are enabled. They rely on the famous [nlohmann](https://github.com/nlohmann/json) library, which is internally fetched and linked.\nIf you don't need such functionalities, put the CMake option **BUILD_EFG_JSON_CONVERTER** to **OFF**.\n\n### VISUAL STUDIO COMPATIBILITY\n\nThis library exploits virtual inheritance to define some objects hierarchies. This might trigger [this](https://stackoverflow.com/questions/6864550/c-inheritance-via-dominance-warning) weird warning when compiling in Windows with Visual Studio. You can simply ignore it or tell Visual Studio to ignore warning code 4250, which is something that can be done as explained [here](https://docs.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-3-c4996?view=msvc-170).\n    \n\n## USAGE\n\nHaven't yet left a **star**? Do it now! :).\n\nFor convenience, assume all these namespaces are used by default:\n```cpp\nusing namespace EFG;\nusing namespace EFG::categoric;\nusing namespace EFG::factor;\nusing namespace EFG::model;\nusing namespace EFG::io;\nusing namespace EFG::train;\nusing namespace EFG::strct;\n```\n\n### FACTORS CONSTRUCTION\n\n**EFG** allows you to define categoric variables and factors by calling simple functions.\nThis is what you would do to build a couple of variables:\n```cpp\n    // define a couple of variables, with the same size\n    VariablePtr A = make_variable(3, \"A\"); // size is 3\n    VariablePtr B = make_variable(3, \"B\"); // size is 3\n```\n\nSuch variables can be referred by the factors correlating them. In order to build a simple correlating factor this is what you would do:\n```cpp\n    // build a simply correlating factor connecting the 2 variables\n    Factor factor_AB(VariablesSoup{B, A}, // the order in the specified\n                                          // group matters: B is assumed\n                                          // as the first variable, A\n                                          // will be the second\n                     Factor::SimplyCorrelatedTag{});\n```\n\nAnd this is what you would do to generate an exponential simple correlating factor:\n```cpp\n    // build an exponential factor using as base `factor_AB`: values of the\n    // images are assumed as exp(weight * images_factor_AB)\n    FactorExponential factor_AB_exponential(\n        factor_AB,\n        1.5f // this will be the value assumed for the weight\n);\n```\n\nYou can also define custom factors, specifying the shape function that maps the values in their domain with their images.\nFor example:\n```cpp\n    // define another variable\n    VariablePtr C = make_variable(2, \"C\"); // size is 2\n    // define a factor connecting C to B\n    // we start building an empty factor, having all images equal to 0\n    Factor factor_BC(VariablesSoup{B, C});\n    // set some individual images of factor_BC\n    // set for \u003c0,1\u003e -\u003e 2\n    factor_BC.set(std::vector\u003cstd::size_t\u003e{0, 1}, 2.f);\n    // set for \u003c2,0\u003e -\u003e 1.3f\n    factor_BC.set(std::vector\u003cstd::size_t\u003e{2, 0}, 1.3f);\n```\n\n### MODELS CONSTRUCTION\n\nFactor graphs can be built incrementally, passing one by one the factors that compose them.\nWithout loss of generality suppose to start from an empty **random field**:\n```cpp\n    // start building an empty random field\n    RandomField model;\n```\n\nthen, you can build some factors and enrich the model with them:\n```cpp\n    // define some variables, which will be later connected\n    auto A = make_variable(4, \"varA\");\n    auto B = make_variable(4, \"varB\");\n    auto C = make_variable(4, \"varC\");\n\n    // without loss of generality, add to the model some simply correlating\n    // factors\n    model.addConstFactor(std::make_shared\u003cFactor\u003e(\n        VariablesSoup{A, B},\n        Factor::SimplyCorrelatedTag{})); // the generated smart\n                                         // pointer is shallow\n                                         // copied\n    model.copyConstFactor(\n        Factor{VariablesSoup{A, C},\n               Factor::SimplyCorrelatedTag{}}); // the passed factor is\n                                                // deep-copied into the\n                                                // model\n```\n\nThe previously added factor are kept constant in the model. In order to enrich the model with a tunable factor you can call a different method:\n```cpp\n    // build some additional tunable exponential factors that will be too added\n    auto factor_exp_BC = std::make_shared\u003cFactorExponential\u003e(\n        Factor{VariablesSoup{B, C}, Factor::SimplyCorrelatedTag{}}, 1.f);\n    model.addTunableFactor(factor_exp_BC);\n\n    auto D = make_variable(4, \"varD\");\n    auto factor_exp_CD = std::make_shared\u003cFactorExponential\u003e(\n        Factor{VariablesSoup{C, D}, Factor::SimplyCorrelatedTag{}}, 1.5f);\n    model.addTunableFactor(factor_exp_CD);\n```\n\nYou can also add a tunable factor, that must share its weigth with an already inserted factor of the model:\n```cpp\n    // insert another tunable factor, this time specifying that it needs to\n    // share the weight with already inserted exponential factor that connects B\n    // and C\n    model.addTunableFactor(\n        std::make_shared\u003cFactorExponential\u003e(\n            Factor{VariablesSoup{C, D}, Factor::SimplyCorrelatedTag{}},\n            2.f // actually this value is irrelevant, as the weight of\n                // factor_exp_BC will be assumed from now on\n            ),\n        VariablesSet{B, C}\n        // this additional input is to specify that this exponential factor\n        // needs to share the weight with the one connecting B and C\n    );\n```\n\nYou can also import the entire graph defined in an xml file:\n```cpp\n    // absorb the structure defined in an xml file\n    xml::Importer::importFromFile(model, std::string{\"file_name.xml\"});\n```\ncheck the documentation or the samples for the expected format the xml file should be compliant with.\n\nSimilarly, you can also import the structure defined in a **json**\n```cpp\n    // absorb the structure encoded in a json string\n    nlohmann::json json_defining_a_structure = ...;\n    json::Importer::importFromJson(model, json_defining_a_structure);\n```\ncheck the documentation or the samples for the expected format the json should be compliant with.\n\n### QUERY THE MODEL\n\nA generated model can be queried in many ways.\nHowever, any query that you can do, is conditioned to the latest set of evidences. \n\nSetting the evidences can be easily done by calling:\n```cpp\n    // set some evidences\n    model.setEvidence(\"variable_1\", 0); // setting variable_1 = 0\n    model.setEvidence(\"variable_2\", 2); // setting variable_2 = 2\n```\n\nYou can get the **conditioned marginal distribution** of a variable by calling:\n```cpp\n    // get the marginal conditioned distribution of an hidden variable\n    std::vector\u003cfloat\u003e conditioned_marginals =\n        model.getMarginalDistribution(\"var_A\");\n```\n\nOr you might be interested in the **maximum a posteriori estimation** of the entire evidence set:\n```cpp\n    // get maxiomum a posteriori estimation of the entire hidden set\n    std::vector\u003cstd::size_t\u003e MAP_hidden_set = model.getHiddenSetMAP();\n```\n\nAs already mentioned, results are subjected to the latest evidences set (which can be also empty).\nOf course, you can update the evidences and get the updated marginals:\n```cpp\n    // set some new evidences\n    model.removeAllEvidences();\n    model.setEvidence(\"evid_1\", 1);\n\n    // compute new conditioned marginals: the should be different as the\n    // evidences were changed\n    conditioned_marginals = model.getMarginalDistribution(\"var_A\");\n```\n\n### TUNE THE MODEL\n\nTunable models are characterized by the exponential factors added to the model itself. Such kind of modelscan be **trained**.\nThis is done by relying on a training set, which can be for example imported from a file:\n```cpp\n    // assume we have a training set for the model stored in a file\n    TrainSet training_set = import_train_set(\"file_name.txt\");\n```\n\nThen, a training approach must be chosen. You can rely on one of the ready to use approaches implemented in [this](https://github.com/andreacasalino/TrainingTools) (by default) fetched package. \nSuppose you want to use a quasi Newton method:\n```cpp\n    // we can train the model using one of the ready to use gradient based\n    // approaches\n    ::train::QuasiNewton ready_to_use_trainer;\n    ready_to_use_trainer.setMaxIterations(50);\n```\n\nThen, you are ready to train the model:\n```cpp\n    // some definitions to control the training process\n    TrainInfo info = TrainInfo{\n        4,  // threads to use\n        1.f // stochasticity. When set different from 1, the stochastich\n            // gradient descend approaches are actually used\n    };\n\n    train_model(tunable_model, ready_to_use_trainer, training_set, info);\n```\n\n### GIBBS SAMPLING\n\nSometimes, it might be useful to draw samples from the model. This can be done with the Gibbs sampling strategy provided by **EFG**:\n```cpp\n    // some definitions to control the samples generation process\n    GibbsSampler::SamplesGenerationContext info =\n        GibbsSampler::SamplesGenerationContext{\n            1000, // samples number\n            0,    // seed used by random engines\n            500   // number of iterations to discard at the beginning (burn out)\n        };\n\n    // get samples from the model using Gibbs sampler\n    std::vector\u003cstd::vector\u003cstd::size_t\u003e\u003e samples =\n        model.makeSamples(info,\n                          4 // threads to use\n        );\n```\n\n## EFG GUI\n\nIf you have found this library useful, please find the time to leave a star :). Just before you go, be aware that [Easy-Factor-Graph-GUI](https://github.com/andreacasalino/Easy-Factor-Graph-GUI) wraps this library as C++ backend to a nice graphical user interactive application:\n\n![What you should see when running the application](https://github.com/andreacasalino/Easy-Factor-Graph-GUI/blob/master/Example.png)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandreacasalino%2Feasy-factor-graph","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fandreacasalino%2Feasy-factor-graph","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandreacasalino%2Feasy-factor-graph/lists"}