{"id":26888809,"url":"https://github.com/Nazari/exun","last_synced_at":"2025-03-31T20:01:26.511Z","repository":{"id":62429498,"uuid":"302016916","full_name":"Nazari/exun","owner":"Nazari","description":"Symbolic math for elixir. Units, Pattern matching, Derivate, Integrate, Multiprocess and more","archived":false,"fork":false,"pushed_at":"2024-02-19T11:41:20.000Z","size":248,"stargazers_count":4,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-05T12:08:57.918Z","etag":null,"topics":["ast","elixir","expr"],"latest_commit_sha":null,"homepage":"","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Nazari.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-10-07T11:36:12.000Z","updated_at":"2024-03-16T07:01:18.000Z","dependencies_parsed_at":"2022-11-01T20:04:31.466Z","dependency_job_id":null,"html_url":"https://github.com/Nazari/exun","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nazari%2Fexun","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nazari%2Fexun/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nazari%2Fexun/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nazari%2Fexun/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Nazari","download_url":"https://codeload.github.com/Nazari/exun/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246531968,"owners_count":20792736,"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":["ast","elixir","expr"],"created_at":"2025-03-31T20:01:25.272Z","updated_at":"2025-03-31T20:01:26.505Z","avatar_url":"https://github.com/Nazari.png","language":"Elixir","funding_links":[],"categories":["Elixir"],"sub_categories":[],"readme":"# Exun\n\nSymbolic math library for Elixir, with unit support.\n\n*Beta state.*\n\nCannot belive in Elixir 1-0.8 == 0.19999999999999996\n\nThis is a big deal, if you can't trust in this basic operation\nyou have a problem. I've reformulated {:numb,b} to {:numb, numerator, denominator}\nin a try to keep accuracy for the usual operations, specially for exponents algebraic.\n\n'eval new \"1-0.8\"' returns \"0.2\", and 'eval new(\"0.1+0.1+0.1\")' returns \"0.3\", \nthe same operation in iex returns \"0.30000000000000004\".\n\nAll test passed. Version 0.5.0 is renewed completely for programatic use. Created Exun struct that holds ast and context,\nand created to_string and inspect on it. Maybe i can create macros to use *,+,-,/ with %Exun{}\n\nTODO:\n - Temperature unit conversions\n - Summatory\n - End Matrix and Vector support. \n\nDONE:\n + Symbolic math pattern match expressions\n + Derivate\n + Simple integration (pol, trig, ln), miss parts and subst\n + Units (factorize, conversion, operation, user definition)\n + Context definition for vars and funcs\n + Functions and User functions  \n + Partially implemented Multiprocess, make reductions in parallell via Tasks \n + Define equations, not only expressions: Isolate variables\n + Fractions, to avoid decimal ops\n + Add more testing and revise docs\n \nrun \"iex -S mix\" inside exun dir and type:\n```\nimport Exun\nimport Exun.Unit\n\neval2str \"(1+a)*(a+1)/(a+1)^3\"\n\"1/(1+a)\"\n\neval2str \"1[m]+1[cm]\"\n\"1.01[m]\"\n\nfactorize \"1[A*Kg*m/s^2]\",\"[slug*cm]\"\n\"6.852176585682165[cm*slug*A/s^2]\"\n\n\"120[Km/h]\" |\u003e convert(\"m/s\")\n\"33.333333333333336[m/s]\"\n```\n\nCall Exun.Unit.help for a list of supported units, you can also add new units via context (a map that holds definitions you can use inside expression)\n```\neval \"25[Km/h]+14[myunit^2]\", %{ \"myunit\" =\u003e \"(m/s)^0.5\" }\n\"20.944444444444443[m/s]\"\n```\n\nYou can put 'context' also, passing a map that defines values for variables:\n```\neval2str \"(a+1)^2/b\", %{\"b\"=\u003e\"a+1\"}\n\"1+a\"\n\neval2str \"(a+b)^2/c\", %{\"a\"=\u003e\"20[m]\",\"b\"=\u003e\"2[cm]\",\"c\"=\u003e\"3[s^2]\"}\n\"133.60013333333333[m^2/s^2]\"\n```\n\nDerivate and support some functions (trigonometrics, hyperbolics, ln):\nOperator ' is derivate, so \"f'x\" is df(x)/dx; rule \"expr'var\" means derivate 'expr' for 'var'. \n```\neval2str \"((1+x)^2)'x\"\n\"2*(1+x)\"\n\neval2str \"sin(2*x)'x\"\n\"2*cos(2*x)\"\n\neval2str \"(x^2+x)'x+1\"\n\"2*(1+x)\"\n```\n\nDefine functions in context\nVars and functions can be named with the same name, like in elixir, arity in a name makes it different so:\n```\nExun.eval2str \"f*f(y)*f(y,3)\", %{\"f\"=\u003e\"3\", \"f(x)\"=\u003e\"x^2\", \"f(a,b)\"=\u003e\"a^2+a*b+b^2\"}\n\"3*y^2*(9+3*y+y^2)\"\n\nExun.eval2str \" f * f(x)'x * f(y)\", %{\"f\"=\u003e\"3\", \"f(x)\"=\u003e\"x^2\"}\n\"6*x*y^2\"\n\n```\n\n Integrate simple expression, not yet implemented Parts or Subst methods. Symbol for integration is $, rule \"$expr,var\" means integrate 'expr' for 'var'\n```\niex(1)\u003e Exun.eval2str \"$3*x^2+2*x+1,x\"\n\"x*(1+x*(1+x))\"\n\niex(5)\u003e Exun.eval2str \"$sin(x),x\"     \n\"-cos(x)\"\n\niex(6)\u003e Exun.eval2str \"$ln(f(x)),x\"\n\"x*ln(f(x))-($(x/f(x)),x)\"\n```\n\nPattern Match expressions in module Pattern:\n```\nimport Exun.Pattern\n\niex(1)\u003e umatch \"u*v'x\",\"x*cos(x)\"\nMatch group ok\n  u     = x\n  v'x   = cos(x)\nMatch group ok\n  u     = x*cos(x)\n  v'x   = 1\nMatch group ok\n  u     = cos(x)\n  v'x   = x\nMatch group ok\n  u     = 1\n  v'x   = x*cos(x)\n\nBuggy...\niex(2)\u003e umatch \"g'x*g^n\", \"3*x^2*(x^3+1)^2\"\nMatch group ok\n  g     = 1+x^3\n  n     = 2\n  g'x   = 3*x^2\n:ok\niex(3)\u003e umatch(\"g(y)+f'x\",\"1+x+y\")\nMatch group ok\n  f     =\u003e x+0.5*x^2\n  f'    =\u003e x+1\n  g     =\u003e y\nMatch group ok\n  f     =\u003e x+0.5*x^2\n  f'    =\u003e 1+x\n  g     =\u003e y\nMatch group ok\n  f     =\u003e 0.5*x^2\n  f'    =\u003e x\n  g     =\u003e y+1\n...\n:ok\niex(4)\u003e umatch(\"f(2*x)\",\"sin(2*x)\")\nMatch group ok\n  f     =\u003e sin\n  x     =\u003e x\n:ok\niex(5)\u003e umatch(\"f*f'x\",\"sin(x)*cos(x)\"\nMatch group ok\n  f     = sin(x)\n:ok\n```\n\nIsolation, Module Exun.Isol can isolate an ast from a tree.\nIt extracts all instances of ast from the equation, so it can return\nmore than one result. Ast can be any valid ast, not only a variable ({:vari, name}) \nFor example:\n```\niex(1)\u003e import Exun.Isol\nExun.Isol\niex(2)\u003e import Exun\nExun\niex(3)\u003e import Exun.UI\nExun.UI\niex(4)\u003e isol (parse_text \"x^2-3=13\"), (parse_text \"x\")\n[ok: {:numb, 4}]\n\niex(5)\u003e mp1=isol (parse_text \"x^2+sin(x)-3=13\"), (parse_text \"x\")\n[\n  ok: {:fcall, \"asin\",\n   [{{:m, :suma}, [numb: 16, minus: {:elev, {:vari, \"x\"}, {:numb, 2}}]}]},\n  ok: {:fcall, \"exp\",\n   [\n     {{:m, :mult},\n      [\n        {:numb, 0.5},\n        {:fcall, \"ln\",\n         [{{:m, :suma}, [numb: 16, minus: {:fcall, \"sin\", [vari: \"x\"]}]}]}\n      ]},\n     {:numb, 2}\n   ]}\n]\n\niex(6)\u003e mp1 |\u003e Enum.map(fn {_,sol} -\u003e tostr(sol)end)\n[\"asin(16-x^2)\", \"exp(0.5*ln(16-sin(x)),2)\"]\n\niex(7)\u003e mp1=isol (parse_text \"x^2+sin(x)-3=13\"), (parse_text \"x^2\")\n[ok: {{:m, :suma}, [numb: 16, minus: {:fcall, \"sin\", [vari: \"x\"]}]}]\n\niex(8)\u003e mp1 |\u003e Enum.map(fn {_,sol} -\u003e tostr(sol)end)                           \n[\"16-sin(x)\"]\n\n```\n\nI've createt the vector and matrix types in yecc, and a module Exun.Matrix, want to\ncreate basic algebraic (+,-,*,/) for those concepts and may be eigenvalues for nxn symbolic matrices. The elements for\nnow are symbolic also, so you will be able to write something like the example below. The symbols\nfor matrix and vector are '{}'.\n```\niex\u003e mat = Exun.new \"{{x^2,x,1},{sin(x),cos(x),tan(x)},{1,2,3}}\"\n#Exun  {{x^2,x,1},{sin(x),cos(x),tan(x)},{1,2,3}}\n\niex\u003e import Exun.Matrix\niex\u003e import Exun.UI\niex\u003e det mat\n#Exun  -(cos(x)*-tan(x)+cos(x)*tan(x))-(x*cos(x)*-tan(x)+x*cos(x)*tan(x))\n\niex\u003e Exun.Matrix.uni_m 4\n{{:unity, 4, 4}, nil}\n\niex\u003e Exun.Matrix.pol_m [{:numb,1,1},{:numb,2,1},{:numb,3,1},{:vari,\"x\"}]\n{{:polynom, 3, 3},\n [\n   {:elev, {:vari, \"x\"}, {:numb, -1}},\n   {{:m, :mult}, [{:numb, 2}, {:elev, {:vari, \"x\"}, {:numb, -1}}]},\n   {{:m, :mult}, [{:numb, 3}, {:elev, {:vari, \"x\"}, {:numb, -1}}]}\n ]}\n```\n\nMultiprocess. Base measurement for speed will be the brutal expression:\n```\niex(5)\u003e :timer.tc(Exun,:eval,[\"(g(a^b,b^a)/g(b^a,a^b))'a\", %{\"g(x,y)\"=\u003e\"(x^y/ln(sinh(y^x))+y^tanh(x)/cos(x*y))'x'y'x\"}])\n{5327979,\n \"(-(-4*(-2*(-a^b^(1+a)*b^a^(1+b)/sinh(b^a^(1+b))*cosh(b^a^(1+b))*ln(b\" \u003c\u003e ...}\n ```\n\n\nIf you are interested in parsing, use 'new' to create an Exun struct, and its field 'ast' to show the expression AST.\n```\niex(2)\u003e exp = Exun.new \"(a+b)^2/c\"\n#Exun  ((a+b)^2)/c\niex(3)\u003e exp.ast\n{{:m, :mult},\n [\n   {:elev, {:vari, \"c\"}, {:numb, -1, 1}},\n   {:elev, {{:m, :suma}, [vari: \"a\", vari: \"b\"]}, {:numb, 2, 1}}\n ]}\n```\n\nThis library use an AST built with erlang's yecc parser and transformation in elixir like this:\n```\n  def mk({:suma, a, @zero}), do: mk(a)\n  def mk({:suma, {:numb, n1}, {:numb, n2}}), do: {:numb, n1 + n2}\n  def mk({:suma, {:numb, _}, {:unit, _, _}}), do: throw(@invalid_unit_operation)\n```\n\n\n## Installation\n\nIf [available in Hex](https://hex.pm/docs/publish), the package can be installed\nby adding `exun` to your list of dependencies in `mix.exs`:\n\n```elixir\ndef deps do\n  [\n    {:exun, \"~\u003e 0.4.4\"}\n  ]\nend\n```\n\nDocumentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)\nand published on [HexDocs](https://hexdocs.pm). Once published, the docs can\nbe found at [https://hexdocs.pm/exun](https://hexdocs.pm/exun).\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FNazari%2Fexun","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FNazari%2Fexun","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FNazari%2Fexun/lists"}