{"id":32175994,"url":"https://github.com/othoz/paragraph","last_synced_at":"2026-02-22T19:03:44.485Z","repository":{"id":57450695,"uuid":"217294295","full_name":"Othoz/paragraph","owner":"Othoz","description":"A computation graph micro-framework providing seamless lazy and concurrent evaluation","archived":false,"fork":false,"pushed_at":"2020-02-14T13:37:05.000Z","size":163,"stargazers_count":16,"open_issues_count":2,"forks_count":0,"subscribers_count":6,"default_branch":"master","last_synced_at":"2026-02-19T23:18:20.404Z","etag":null,"topics":["computation-graph","concurrent","lazy"],"latest_commit_sha":null,"homepage":"https://paragraph.readthedocs.io/","language":"Python","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/Othoz.png","metadata":{"files":{"readme":"README.rst","changelog":"CHANGELOG.rst","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-10-24T12:31:22.000Z","updated_at":"2025-12-05T03:44:36.000Z","dependencies_parsed_at":"2022-09-26T17:31:33.470Z","dependency_job_id":null,"html_url":"https://github.com/Othoz/paragraph","commit_stats":null,"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"purl":"pkg:github/Othoz/paragraph","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Othoz%2Fparagraph","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Othoz%2Fparagraph/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Othoz%2Fparagraph/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Othoz%2Fparagraph/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Othoz","download_url":"https://codeload.github.com/Othoz/paragraph/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Othoz%2Fparagraph/sbom","scorecard":{"id":106266,"data":{"date":"2025-08-11","repo":{"name":"github.com/Othoz/paragraph","commit":"38934e982089bd8cdcf83bc9f89ffc36071367a7"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":5,"checks":[{"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":"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":"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":"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":10,"reason":"all changesets reviewed","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":"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":"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":"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":"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":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"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":"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":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 30 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-15T11:09:29.629Z","repository_id":57450695,"created_at":"2025-08-15T11:09:29.629Z","updated_at":"2025-08-15T11:09:29.629Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29723574,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-22T15:10:41.462Z","status":"ssl_error","status_checked_at":"2026-02-22T15:10:04.636Z","response_time":110,"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":["computation-graph","concurrent","lazy"],"created_at":"2025-10-21T19:52:54.567Z","updated_at":"2026-02-22T19:03:44.479Z","avatar_url":"https://github.com/Othoz.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"Paragraph\n=========\n\nA pure Python micro-framework supporting seamless lazy and concurrent evaluation of computation graphs.\n\n.. image:: https://img.shields.io/pypi/v/paragraph.svg\n    :target: https://pypi.org/project/paragraph/\n\n.. image:: https://img.shields.io/pypi/pyversions/paragraph.svg\n    :target: https://pypi.org/project/paragraph/\n\n.. image:: https://travis-ci.org/Othoz/paragraph.svg?branch=master\n    :target: https://travis-ci.org/Othoz/paragraph\n\n.. image:: https://readthedocs.org/projects/paragraph/badge/?version=latest\n    :target: https://paragraph.readthedocs.io/en/latest/?badge=latest\n\n.. image:: https://api.codacy.com/project/badge/Coverage/9f126473b30c47f6ba8a14b2ebea48a1\n    :target: https://www.codacy.com/manual/Othoz/paragraph?utm_source=github.com\u0026amp;utm_medium=referral\u0026amp;utm_content=Othoz/paragraph\u0026amp;utm_campaign=Badge_Coverage\n\n.. image:: https://api.codacy.com/project/badge/Grade/9f126473b30c47f6ba8a14b2ebea48a1\n    :target: https://www.codacy.com/manual/Othoz/paragraph?utm_source=github.com\u0026amp;utm_medium=referral\u0026amp;utm_content=Othoz/paragraph\u0026amp;utm_campaign=Badge_Grade\n\n\nIntroduction\n''''''''''''\n\nParagraph adds the *functional programming paradigm* to Python in a minimal fashion. One additional class, ``Variable``, and a\nfunction decorator, ``op``, is all it takes to turn regular Python code into a *computation graph*, i.e. a computer representation of a system of\nequations:\n\n\u003e\u003e\u003e import paragraph as pg\n\u003e\u003e\u003e import operator\n\u003e\u003e\u003e x, y = pg.Variable(\"x\"), pg.Variable(\"y\")\n\u003e\u003e\u003e add = pg.op(operator.add)\n\u003e\u003e\u003e s = add.op(x, y)\n\n\nThe few lines above fully instantiate a computation graph, here in its simplest form with just one equation relating ``x``, ``y`` and ``s`` via the function\n``add``. Given values for the input variables ``x`` and ``y``, the value of ``s`` is resolved as follows:\n\n\u003e\u003e\u003e pg.evaluate([s], {x: 5, y: 10})\n[15]\n\n\nKey features\n''''''''''''\n\nThe main benefits of using paragraph stem from the following features of ``pg.session.evaluate``:\n\nLazy evaluation\n  Irrespective of the size of the computation graph, only the operations required to evaluate the output variables are executed. Consider the following\n  extension of the above graph:\n\n  \u003e\u003e\u003e z = pg.Variable(\"z\")\n  \u003e\u003e\u003e t = add.op(y, z)\n\n  Then the statement:\n\n  \u003e\u003e\u003e pg.evaluate([t], {y: 10, z: 50})\n  [60]\n\n  just ignores the variables ``s`` and ``x`` altogether, since they do not contribute to the evaluation of ``t``. In particular, the operation ``add(x, y)``\n  is not executed.\n\nEager currying\n  Invoking an op with invariable arguments (that is, arguments that are not of type ``Variable``) just returns an invariable value: evaluation is\n  eager whenever possible. If invariable arguments are provided for a subset of the input variables, the computation graph can be simplified using ``solve``,\n  which returns a new variable:\n  \n  \u003e\u003e\u003e u_x = pg.solve([u], {y: 10, z: 50})[0]\n  \n  Here, ``u_x`` is a different variable from ``u``: it now depends on a single input variable (``x``), and it knows nothing about a variable ``y`` or ``z``,\n  instead storing a reference to the value of their sum ``t``, i.e. ``60``.\n\n  Thus, ``pg.session.solve`` acts much as ``functools.partial``, except it simplifies the system of equations where possible by executing dependent\n  operations whose arguments are invariable.\n\nGraph composition\n  Assume a variable ``y`` depends on a number of input variables ``x_1``,..., ``x_p``, and another variable ``v`` on ``u_1``,...,``u_q`` (not necessarily\n  different), and ``v`` should be identified to ``x_p``. The following statement:\n\n  \u003e\u003e\u003e y_v = pg.solve([y], args={x_p: v})[0]\n\n  returns a new variable ``y_v`` that depends on ``x_1``,..., ``x_{p-1}`` as well as on ``u_1``,..., ``u_q``, as if the two computation graphs defining ``y``\n  and ``v`` had been pieced together.\n\n  Note that the respective input variables may overlap, with the restriction that ``v`` should not depend on ``x_p`` as that would result in a circular\n  dependency. Also, additional arguments may be added to ``args`` in the statement above to set further values of the input variables ``x_1``,...,\n  ``x_{p-1}``. However, values cannot be set for ``u_1``,..., ``u_q`` here, since they are not dependencies of ``y``, but of ``y_v``.\n\nTransparent multithreading\n  Invoking ``evaluate`` or ``solve`` with an instance of ``concurrent.ThreadPoolExecutor`` will allow independent blocks of the computation graph to run in\n  separate threads:\n\n  \u003e\u003e\u003e with ThreadPoolExecutor as ex:\n  ...     res = pg.evaluate([z_t], {t: 5}, ex)\n\n  This is particularly beneficial if large subsets of the graph are independent.\n\n\nConstraints\n'''''''''''\n\nSide-effects\n------------\n\nThe features listed above come at some price, essentially because the order in which operations are actually executed generally differs from the order of\ntheir invocations. For paragraph to guarantee that a variable always evaluates to the same value given the same inputs, as in a system of mathematical\nequations, it is paramount that operations remain free of side-effects, i.e. they **never** mutate an object they received as an argument, or store as an\nattribute. The state sequence of the object would be, by definition, out of the control of the programmer.\n\nThere is close to nothing paragraph can do to prevent such a thing happening. When in doubt, make sure to operate on a copy of the argument.\n\nTyping\n------\n\nVariables do not carry any information regarding the type of the value they represent, which precludes binding a method of the underlying value to an\ninstance of ``Variable``: such instructions can appear only within the code of an op. Since binary operators are implemented using special methods in\nPython, this also precludes such statements as:\n\n\u003e\u003e\u003e s = x + y\n\nfor this would be resolved by the Python interpreter into ``s = x.__add__(y)``, then ``s = y.__radd__(x)``, yet none of these methods is defined by\n``Variable``.\n\nFor more information please consult the `documentation \u003chttp://paragraph.readthedocs.io\u003e`_.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fothoz%2Fparagraph","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fothoz%2Fparagraph","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fothoz%2Fparagraph/lists"}