ddss 0.0.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
ddss-0.0.6/PKG-INFO ADDED
@@ -0,0 +1,15 @@
1
+ Metadata-Version: 2.4
2
+ Name: ddss
3
+ Version: 0.0.6
4
+ Summary: Distributed Deductive System Sorts
5
+ Requires-Python: >=3.13
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: aiomysql>=0.3.2
8
+ Requires-Dist: aiosqlite>=0.22.0
9
+ Requires-Dist: apyds>=0.0.7
10
+ Requires-Dist: apyds-bnf>=0.0.7
11
+ Requires-Dist: cloudpickle>=3.1.2
12
+ Requires-Dist: egglog>=12.0.0
13
+ Requires-Dist: sqlalchemy>=2.0.45
14
+
15
+ # Distributed Deductive System Sorts
ddss-0.0.6/README.md ADDED
@@ -0,0 +1 @@
1
+ # Distributed Deductive System Sorts
ddss-0.0.6/ddss/ds.py ADDED
@@ -0,0 +1,49 @@
1
+ import sys
2
+ import asyncio
3
+ from sqlalchemy import select
4
+ from apyds import Search
5
+ from .orm import initialize_database, insert_or_ignore, Facts, Ideas
6
+ from .poly import Poly
7
+
8
+
9
+ async def main(addr, engine=None, session=None):
10
+ if engine is None or session is None:
11
+ engine, session = await initialize_database(addr)
12
+
13
+ search = Search()
14
+ max_fact = -1
15
+
16
+ while True:
17
+ begin = asyncio.get_running_loop().time()
18
+
19
+ async with session() as sess:
20
+ for i in await sess.scalars(select(Facts).where(Facts.id > max_fact)):
21
+ max_fact = max(max_fact, i.id)
22
+ search.add(Poly(dsp=i.data).ds)
23
+ tasks = []
24
+
25
+ def handler(rule):
26
+ poly = Poly(rule=rule)
27
+ tasks.append(asyncio.create_task(insert_or_ignore(sess, Facts, poly.dsp)))
28
+ if idea := poly.idea:
29
+ tasks.append(asyncio.create_task(insert_or_ignore(sess, Ideas, idea.dsp)))
30
+ return False
31
+
32
+ count = search.execute(handler)
33
+ await asyncio.gather(*tasks)
34
+ await sess.commit()
35
+
36
+ end = asyncio.get_running_loop().time()
37
+ duration = end - begin
38
+ if count == 0:
39
+ delay = max(0, 0.1 - duration)
40
+ await asyncio.sleep(delay)
41
+
42
+ await engine.dispose()
43
+
44
+
45
+ if __name__ == "__main__":
46
+ if len(sys.argv) != 2:
47
+ print(f"Usage: {sys.argv[0]} <database-addr>")
48
+ sys.exit(1)
49
+ asyncio.run(main(sys.argv[1]))
ddss-0.0.6/ddss/egg.py ADDED
@@ -0,0 +1,47 @@
1
+ import sys
2
+ import asyncio
3
+ from sqlalchemy import select
4
+ from .orm import initialize_database, insert_or_ignore, Facts, Ideas
5
+ from .egraph import Search
6
+ from .poly import Poly
7
+
8
+
9
+ async def main(addr, engine=None, session=None):
10
+ if engine is None or session is None:
11
+ engine, session = await initialize_database(addr)
12
+
13
+ search = Search()
14
+ pool = []
15
+ max_fact = -1
16
+ max_idea = -1
17
+
18
+ while True:
19
+ count = 0
20
+ begin = asyncio.get_running_loop().time()
21
+
22
+ async with session() as sess:
23
+ for i in await sess.scalars(select(Facts).where(Facts.id > max_fact)):
24
+ max_fact = max(max_fact, i.id)
25
+ search.add(Poly(dsp=i.data))
26
+ for i in await sess.scalars(select(Ideas).where(Ideas.id > max_idea)):
27
+ max_idea = max(max_idea, i.id)
28
+ pool.append(Poly(dsp=i.data))
29
+ tasks = []
30
+ for i in pool:
31
+ for o in search.execute(i):
32
+ tasks.append(asyncio.create_task(insert_or_ignore(sess, Facts, o.dsp)))
33
+ await asyncio.gather(*tasks)
34
+ await sess.commit()
35
+
36
+ end = asyncio.get_running_loop().time()
37
+ duration = end - begin
38
+ if count == 0:
39
+ delay = max(0, 0.1 - duration)
40
+ await asyncio.sleep(delay)
41
+
42
+
43
+ if __name__ == "__main__":
44
+ if len(sys.argv) != 2:
45
+ print(f"Usage: {sys.argv[0]} <database-addr>")
46
+ sys.exit(1)
47
+ asyncio.run(main(sys.argv[1]))
@@ -0,0 +1,83 @@
1
+ from __future__ import annotations
2
+ import typing
3
+ import egglog
4
+ from apyds import Term, List
5
+ from .poly import Poly
6
+
7
+
8
+ class EGraphTerm(egglog.Expr):
9
+ def __init__(self, name: egglog.StringLike) -> None: ...
10
+
11
+ @classmethod
12
+ def begin(cls) -> EGraphTerm: ...
13
+
14
+ @classmethod
15
+ def pair(cls, lhs: EGraphTerm, rhs: EGraphTerm) -> EGraphTerm: ...
16
+
17
+
18
+ def _ds_to_egraph(data: Term) -> EGraphTerm:
19
+ term = data.term
20
+ if isinstance(term, List):
21
+ result = EGraphTerm.begin()
22
+ for i in range(len(term)):
23
+ child = _ds_to_egraph(term[i])
24
+ result = EGraphTerm.pair(result, child)
25
+ return result
26
+ else:
27
+ return EGraphTerm(str(term))
28
+
29
+
30
+ class Search:
31
+ def __init__(self) -> None:
32
+ self.egraph = egglog.EGraph()
33
+ self.terms = set()
34
+
35
+ def _is_equality(self, data: Poly) -> bool:
36
+ return data.ds.startswith("----\n(binary == ")
37
+
38
+ def _extract_lhs_rhs(self, data: Poly) -> tuple[str, str]:
39
+ term = data.rule.conclusion
40
+ lhs = str(term.term[2])
41
+ rhs = str(term.term[3])
42
+ return lhs, rhs
43
+
44
+ def _ast(self, data: str) -> EGraphTerm:
45
+ result = _ds_to_egraph(Term(data))
46
+ self.egraph.register(result)
47
+ return result
48
+
49
+ def _set_equality(self, lhs: str, rhs: str) -> None:
50
+ self.egraph.register(egglog.union(self._ast(lhs)).with_(self._ast(rhs)))
51
+
52
+ def _get_equality(self, lhs: str, rhs: str) -> None:
53
+ return self.egraph.check_bool(self._ast(lhs) == self._ast(rhs))
54
+
55
+ def _search_equality(self, data: str) -> typing.Iterator[str]:
56
+ for result in self.terms:
57
+ if self._get_equality(data, result):
58
+ yield result
59
+
60
+ def _build_equality(self, lhs: str, rhs: str) -> Poly:
61
+ return Poly(ds=f"----\n(binary == {lhs} {rhs})")
62
+
63
+ def add(self, data: Poly) -> None:
64
+ if not self._is_equality(data):
65
+ return
66
+ lhs, rhs = self._extract_lhs_rhs(data)
67
+ self.terms.add(lhs)
68
+ self.terms.add(rhs)
69
+ self._set_equality(lhs, rhs)
70
+
71
+ def execute(self, data: Poly) -> typing.Iterator[Poly]:
72
+ if not self._is_equality(data):
73
+ return
74
+ lhs, rhs = self._extract_lhs_rhs(data)
75
+ if self._get_equality(lhs, rhs):
76
+ yield self._build_equality(lhs, rhs)
77
+ return
78
+ if lhs.startswith("`"):
79
+ for result in self._search_equality(rhs):
80
+ yield self._build_equality(result, rhs)
81
+ if rhs.startswith("`"):
82
+ for result in self._search_equality(lhs):
83
+ yield self._build_equality(lhs, result)
@@ -0,0 +1,32 @@
1
+ import sys
2
+ import asyncio
3
+ from .orm import initialize_database, insert_or_ignore, Facts, Ideas
4
+ from .poly import Poly
5
+
6
+
7
+ async def main(addr, engine=None, session=None):
8
+ if engine is None or session is None:
9
+ engine, session = await initialize_database(addr)
10
+
11
+ while True:
12
+ try:
13
+ data = await asyncio.get_running_loop().run_in_executor(None, input)
14
+ if data.strip() == "":
15
+ continue
16
+ except EOFError:
17
+ break
18
+ async with session() as sess:
19
+ poly = Poly(dsp=data)
20
+ await insert_or_ignore(sess, Facts, poly.dsp)
21
+ if idea := poly.idea:
22
+ await insert_or_ignore(sess, Ideas, idea.dsp)
23
+ await sess.commit()
24
+
25
+ await engine.dispose()
26
+
27
+
28
+ if __name__ == "__main__":
29
+ if len(sys.argv) != 2:
30
+ print(f"Usage: {sys.argv[0]} <database-addr>")
31
+ sys.exit(1)
32
+ asyncio.run(main(sys.argv[1]))
@@ -0,0 +1,34 @@
1
+ import sys
2
+ import asyncio
3
+ from .orm import initialize_database
4
+ from .ds import main as ds
5
+ from .egg import main as egg
6
+ from .input import main as input
7
+ from .output import main as output
8
+
9
+
10
+ async def main(addr, recreate=False):
11
+ engine, session = await initialize_database(addr, recreate)
12
+ await asyncio.gather(
13
+ ds(addr, engine, session),
14
+ egg(addr, engine, session),
15
+ input(addr, engine, session),
16
+ output(addr, engine, session),
17
+ )
18
+
19
+
20
+ def cli():
21
+ if len(sys.argv) == 1:
22
+ addr = "sqlite+aiosqlite:////tmp/ddss.db"
23
+ recreate = True
24
+ elif len(sys.argv) == 2:
25
+ addr = sys.argv[1]
26
+ recreate = False
27
+ else:
28
+ print(f"Usage: {sys.argv[0]} [<database-addr>]")
29
+ sys.exit(1)
30
+ asyncio.run(main(addr, recreate))
31
+
32
+
33
+ if __name__ == "__main__":
34
+ cli()
ddss-0.0.6/ddss/orm.py ADDED
@@ -0,0 +1,45 @@
1
+ import asyncio
2
+ from collections import defaultdict
3
+ from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker
4
+ from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession
5
+ from sqlalchemy.exc import IntegrityError
6
+ from sqlalchemy import Integer, Text
7
+ from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
8
+
9
+
10
+ class Base(DeclarativeBase):
11
+ pass
12
+
13
+
14
+ class Facts(Base):
15
+ __tablename__ = "facts"
16
+ id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
17
+ data: Mapped[str] = mapped_column(Text, unique=True, nullable=False)
18
+
19
+
20
+ class Ideas(Base):
21
+ __tablename__ = "ideas"
22
+ id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
23
+ data: Mapped[str] = mapped_column(Text, unique=True, nullable=False)
24
+
25
+
26
+ async def initialize_database(
27
+ addr: str, recreate: bool = False
28
+ ) -> tuple[AsyncEngine, async_sessionmaker[AsyncSession]]:
29
+ engine = create_async_engine(addr)
30
+ session = async_sessionmaker(engine)
31
+ async with engine.begin() as conn:
32
+ if recreate:
33
+ await conn.run_sync(Base.metadata.drop_all)
34
+ await conn.run_sync(Base.metadata.create_all)
35
+ return engine, session
36
+
37
+
38
+ async def insert_or_ignore(sess: AsyncSession, model, data, locks=defaultdict(lambda: asyncio.Lock())) -> None:
39
+ async with locks[id(sess.bind)]:
40
+ try:
41
+ async with sess.begin_nested():
42
+ sess.add(model(data=data))
43
+ await sess.flush()
44
+ except IntegrityError:
45
+ pass
@@ -0,0 +1,39 @@
1
+ import sys
2
+ import asyncio
3
+ from sqlalchemy import select
4
+ from .orm import initialize_database, Facts, Ideas
5
+ from .poly import Poly
6
+
7
+
8
+ async def main(addr, engine=None, session=None):
9
+ if engine is None or session is None:
10
+ engine, session = await initialize_database(addr)
11
+
12
+ max_fact = -1
13
+ max_idea = -1
14
+
15
+ while True:
16
+ count = 0
17
+ begin = asyncio.get_running_loop().time()
18
+
19
+ async with session() as sess:
20
+ for i in await sess.scalars(select(Facts).where(Facts.id > max_fact)):
21
+ max_fact = max(max_fact, i.id)
22
+ print("fact:", Poly(dsp=i.data).dsp)
23
+ for i in await sess.scalars(select(Ideas).where(Ideas.id > max_idea)):
24
+ max_idea = max(max_idea, i.id)
25
+ print("idea:", Poly(dsp=i.data).dsp)
26
+ await sess.commit()
27
+
28
+ end = asyncio.get_running_loop().time()
29
+ duration = end - begin
30
+ if count == 0:
31
+ delay = max(0, 0.1 - duration)
32
+ await asyncio.sleep(delay)
33
+
34
+
35
+ if __name__ == "__main__":
36
+ if len(sys.argv) != 2:
37
+ print(f"Usage: {sys.argv[0]} <database-addr>")
38
+ sys.exit(1)
39
+ asyncio.run(main(sys.argv[1]))
@@ -0,0 +1,28 @@
1
+ from apyds import Rule
2
+ from apyds_bnf import parse, unparse
3
+
4
+
5
+ class Poly:
6
+ def __init__(self, **kwargs):
7
+ ((key, value),) = kwargs.items()
8
+ match key:
9
+ case "dsp":
10
+ self.ds = parse(value)
11
+ self.dsp = unparse(self.ds)
12
+ self.rule = Rule(self.ds)
13
+ case "ds":
14
+ self.dsp = unparse(value)
15
+ self.ds = parse(self.dsp)
16
+ self.rule = Rule(self.ds)
17
+ case "rule":
18
+ self.rule = value
19
+ self.ds = str(self.rule)
20
+ self.dsp = unparse(self.ds)
21
+ case _:
22
+ raise ValueError("Invalid argument for Poly")
23
+
24
+ @property
25
+ def idea(self):
26
+ if len(self.rule) != 0:
27
+ return Poly(ds=f"--\n{self.rule[0]}")
28
+ return None
@@ -0,0 +1,15 @@
1
+ Metadata-Version: 2.4
2
+ Name: ddss
3
+ Version: 0.0.6
4
+ Summary: Distributed Deductive System Sorts
5
+ Requires-Python: >=3.13
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: aiomysql>=0.3.2
8
+ Requires-Dist: aiosqlite>=0.22.0
9
+ Requires-Dist: apyds>=0.0.7
10
+ Requires-Dist: apyds-bnf>=0.0.7
11
+ Requires-Dist: cloudpickle>=3.1.2
12
+ Requires-Dist: egglog>=12.0.0
13
+ Requires-Dist: sqlalchemy>=2.0.45
14
+
15
+ # Distributed Deductive System Sorts
@@ -0,0 +1,16 @@
1
+ README.md
2
+ pyproject.toml
3
+ ddss/ds.py
4
+ ddss/egg.py
5
+ ddss/egraph.py
6
+ ddss/input.py
7
+ ddss/main.py
8
+ ddss/orm.py
9
+ ddss/output.py
10
+ ddss/poly.py
11
+ ddss.egg-info/PKG-INFO
12
+ ddss.egg-info/SOURCES.txt
13
+ ddss.egg-info/dependency_links.txt
14
+ ddss.egg-info/entry_points.txt
15
+ ddss.egg-info/requires.txt
16
+ ddss.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ ddss = ddss.main:cli
@@ -0,0 +1,7 @@
1
+ aiomysql>=0.3.2
2
+ aiosqlite>=0.22.0
3
+ apyds>=0.0.7
4
+ apyds-bnf>=0.0.7
5
+ cloudpickle>=3.1.2
6
+ egglog>=12.0.0
7
+ sqlalchemy>=2.0.45
@@ -0,0 +1 @@
1
+ ddss
@@ -0,0 +1,21 @@
1
+ [project]
2
+ name = "ddss"
3
+ version = "0.0.6"
4
+ description = "Distributed Deductive System Sorts"
5
+ readme = "README.md"
6
+ requires-python = ">=3.13"
7
+ dependencies = [
8
+ "aiomysql>=0.3.2",
9
+ "aiosqlite>=0.22.0",
10
+ "apyds>=0.0.7",
11
+ "apyds-bnf>=0.0.7",
12
+ "cloudpickle>=3.1.2",
13
+ "egglog>=12.0.0",
14
+ "sqlalchemy>=2.0.45",
15
+ ]
16
+
17
+ [project.scripts]
18
+ ddss = "ddss.main:cli"
19
+
20
+ [tool.ruff]
21
+ line-length = 120
ddss-0.0.6/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+