{"id":13748744,"url":"https://github.com/advancedresearch/linear_solver","last_synced_at":"2026-03-17T21:37:07.105Z","repository":{"id":62442267,"uuid":"157565028","full_name":"advancedresearch/linear_solver","owner":"advancedresearch","description":"A linear solver designed to be easy to use with Rust enums.","archived":false,"fork":false,"pushed_at":"2021-04-11T09:33:58.000Z","size":37,"stargazers_count":33,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2026-01-02T20:55:55.280Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/advancedresearch.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":"bvssvni"}},"created_at":"2018-11-14T14:57:32.000Z","updated_at":"2025-10-10T06:49:03.000Z","dependencies_parsed_at":"2022-11-01T22:15:28.416Z","dependency_job_id":null,"html_url":"https://github.com/advancedresearch/linear_solver","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/advancedresearch/linear_solver","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/advancedresearch%2Flinear_solver","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/advancedresearch%2Flinear_solver/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/advancedresearch%2Flinear_solver/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/advancedresearch%2Flinear_solver/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/advancedresearch","download_url":"https://codeload.github.com/advancedresearch/linear_solver/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/advancedresearch%2Flinear_solver/sbom","scorecard":{"id":168445,"data":{"date":"2025-08-11","repo":{"name":"github.com/advancedresearch/linear_solver","commit":"d6bdfb148c2f6acc6dd1bba54d8500e61329d8f3"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Code-Review","score":0,"reason":"Found 0/16 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 28 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-16T15:39:20.110Z","repository_id":62442267,"created_at":"2025-08-16T15:39:20.110Z","updated_at":"2025-08-16T15:39:20.110Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30632071,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-17T17:32:55.572Z","status":"ssl_error","status_checked_at":"2026-03-17T17:32:38.732Z","response_time":56,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":[],"created_at":"2024-08-03T07:00:48.572Z","updated_at":"2026-03-17T21:37:07.074Z","avatar_url":"https://github.com/advancedresearch.png","language":"Rust","funding_links":["https://github.com/sponsors/bvssvni"],"categories":["Projects"],"sub_categories":["Provers and Solvers"],"readme":"# linear_solver\nA linear solver designed to be easy to use with Rust enums.\n\nThis is a library for automated theorem proving.\n\nLinear solving means that some facts can be replaced with others.\nThis technique can also be used to make theorem proving more efficient.\n\nIf you are looking for a solver that does not remove facts,  \nsee [monotonic_solver](https://github.com/advancedresearch/monotonic_solver)\n\n*Notice! This solver does not support multiple histories.  \nIt assumes that when facts are simplified,\nthey prove the same set of facts without the simplifaction.*\n\nA linear solver can be used to:\n\n- Prove some things in linear logic\n- Prove some things in classical logic more efficiently\n- Prove some things about where resources are \"consumed\"\n- Constraint solving\n- Implement some constraint solving programming language\n\nThis project was heavily inspired by\n[CHR (Constraint Handling Rules)](https://dtai.cs.kuleuven.be/CHR/)\n\n### Example: Walk\n\n```rust\n/*\n\nIn this example, we reduce a walk (left, right, up, down):\n\n    l, l, u, l, r, d, d, r\n    ----------------------\n    l, u\n\n*/\n\nextern crate linear_solver;\n\nuse linear_solver::{solve_minimum, Inference};\nuse linear_solver::Inference::*;\n\nuse std::collections::HashSet;\n\nuse self::Expr::*;\n\n#[derive(Clone, PartialEq, Eq, Debug, Hash)]\npub enum Expr {\n    Left,\n    Right,\n    Up,\n    Down,\n}\n\npub fn infer(cache: \u0026HashSet\u003cExpr\u003e, _facts: \u0026[Expr]) -\u003e Option\u003cInference\u003cExpr\u003e\u003e {\n    // Put simplification rules first to find simplest set of facts.\n    if cache.contains(\u0026Left) \u0026\u0026 cache.contains(\u0026Right) {\n        return Some(ManyTrue {from: vec![Left, Right]});\n    }\n    if cache.contains(\u0026Up) \u0026\u0026 cache.contains(\u0026Down) {\n        return Some(ManyTrue {from: vec![Up, Down]});\n    }\n    None\n}\n\nfn main() {\n    let start = vec![\n        Left,\n        Left,\n        Up,\n        Left,\n        Right,\n        Down,\n        Down,\n        Right,\n    ];\n\n    let res = solve_minimum(start, infer);\n    for i in 0..res.len() {\n        println!(\"{:?}\", res[i]);\n    }\n}\n```\n\n### Example: Less or Equal\n\n```rust\n/*\n\nIn this example, we prove the following:\n\n    X \u003c= Y\n    Y \u003c= Z\n    Z \u003c= X\n    ------\n    Y = Z\n    Y = X\n\n*/\n\nextern crate linear_solver;\n\nuse linear_solver::{solve_minimum, Inference};\nuse linear_solver::Inference::*;\n\nuse std::collections::HashSet;\n\nuse self::Expr::*;\n\n#[derive(Clone, PartialEq, Eq, Debug, Hash)]\npub enum Expr {\n    Var(\u0026'static str),\n    Le(Box\u003cExpr\u003e, Box\u003cExpr\u003e),\n    Eq(Box\u003cExpr\u003e, Box\u003cExpr\u003e),\n}\n\npub fn infer(cache: \u0026HashSet\u003cExpr\u003e, facts: \u0026[Expr]) -\u003e Option\u003cInference\u003cExpr\u003e\u003e {\n    // Put simplification rules first to find simplest set of facts.\n    for ea in facts {\n        if let Le(ref a, ref b) = *ea {\n            if a == b {\n                // (X \u003c= X) \u003c=\u003e true\n                return Some(OneTrue {from: ea.clone()});\n            }\n\n            for eb in facts {\n                if let Le(ref c, ref d) = *eb {\n                    if a == d \u0026\u0026 b == c {\n                        // (X \u003c= Y) ∧ (Y \u003c= X) \u003c=\u003e (X = Y)\n                        return Some(Inference::replace(\n                            vec![ea.clone(), eb.clone()],\n                            Eq(a.clone(), b.clone()),\n                            cache\n                        ))\n                    }\n                }\n            }\n        }\n\n        if let Eq(ref a, ref b) = *ea {\n            for eb in facts {\n                if let Le(ref c, ref d) = *eb {\n                    if c == b {\n                        // (X = Y) ∧ (Y \u003c= Z) \u003c=\u003e (X = Y) ∧ (X \u003c= Z)\n                        return Some(Inference::replace_one(\n                            eb.clone(),\n                            Le(a.clone(), d.clone()),\n                            cache\n                        ));\n                    } else if d == b {\n                        // (X = Y) ∧ (Z \u003c= Y) \u003c=\u003e (X = Y) ∧ (Z \u003c= X)\n                        return Some(Inference::replace_one(\n                            eb.clone(),\n                            Le(c.clone(), a.clone()),\n                            cache\n                        ));\n                    }\n                }\n\n                if let Eq(ref c, ref d) = *eb {\n                    if b == c {\n                        // (X = Y) ∧ (Y = Z) \u003c=\u003e (X = Y) ∧ (X = Z)\n                        return Some(Inference::replace_one(\n                            eb.clone(),\n                            Eq(a.clone(), d.clone()),\n                            cache\n                        ));\n                    }\n                }\n            }\n        }\n    }\n\n    // Put propagation rules last to find simplest set of facts.\n    for ea in facts {\n        if let Le(ref a, ref b) = *ea {\n            for eb in facts {\n                if let Le(ref c, ref d) = *eb {\n                    if b == c {\n                        // (X \u003c= Y) ∧ (Y \u003c= Z) =\u003e (X \u003c= Z)\n                        let new_expr = Le(a.clone(), d.clone());\n                        if !cache.contains(\u0026new_expr) {return Some(Propagate(new_expr))};\n                    }\n                }\n            }\n        }\n    }\n    None\n}\n\npub fn var(name: \u0026'static str) -\u003e Box\u003cExpr\u003e {Box::new(Var(name))}\n\nfn main() {\n    let start = vec![\n        Le(var(\"X\"), var(\"Y\")), // X \u003c= Y\n        Le(var(\"Y\"), var(\"Z\")), // Y \u003c= Z\n        Le(var(\"Z\"), var(\"X\")), // Z \u003c= X\n    ];\n\n    let res = solve_minimum(start, infer);\n    for i in 0..res.len() {\n        println!(\"{:?}\", res[i]);\n    }\n}\n```\n\n### Linear logic\n\nWhen some facts are simplified, e.g.:\n\n```text\nX, Y \u003c=\u003e Z\n```\n\nYou need two new copies of `X` and `Y` to infer another `Z`.\nThis is because the solver does not remove all copies.\n\nWhen doing classical theorem proving with a linear solver,\nit is common to check that every fact is unique in the input,\nand that the `cache` is checked before adding new facts.\nThis ensures that the solver does not add redundant facts.\n\nHowever, when doing linear theorem proving,\none can generate redundant facts `Z` for every `X` and `Y`.\n\n### Meaning of goals\n\nSince a linear solver can both introduce new facts\nand remove them, it means that termination in the sense of proving a\ngoal does not make sense, since the goal can be removed later.\n\nInstead, a goal is considered proved when it belongs to the same \"cycle\".\nThis is the repeated list of sets of facts that follows from\nusing a deterministic solver with rules that stops expanding.\n\nThe minimum set of facts in the cycle is considered the implicit goal,\nbecause all the other facts in the cycle can be inferred from\nthis set of facts.\n\nNotice that this a minimum set of facts in a cycle is different\nfrom a minimum set of axioms. A minimum set of axioms is a set of facts\nthat proves a minimum set of facts with even fewer facts.\nWith other words, the minimum set of axioms starts outside the cycle.\nWhen it moves inside the cycle, it is identical to some minimum set of facts.\n\nBoth the minimum set of facts and the minimum set of axioms can be used\nto identify an equivalence between two sets of facts.\n\n### Intuition of `false` and `true`\n\nThe intuition of `false` can be thought of as:\n\n- Some fact which everything can be proven from\n- Some fact which every contradiction can be simplified to\n- A language that contains a contradiction for every truth value\n\nThe minimum set of facts in a such language,\nwhen a cycle contains `false`, is `false`.\n\nThe intuition of `true` can be thought of as:\n\n- Some fact which every initial fact can be proven from.\n- Some fact that contradicts `false`\n\nThis means that the initial facts implies `true` and\nsince it contradicts `false`, if there exists a contradiction\nin the initial facts, then they can prove `false`.\n\nTherefore a proof from initial facts is `true`\nif it's minimum set of facts does not equals `false`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadvancedresearch%2Flinear_solver","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fadvancedresearch%2Flinear_solver","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadvancedresearch%2Flinear_solver/lists"}