Repository: joshuaguerin/Answer-Set-Programming-Algorithms
Pull request #19: David a
Author: login: joshuaguerin
Labels: name: enhancement
description: New feature or request
Created: 2025-05-21T14:47:58Z | Merged: 2025-05-29T20:50:06Z

First PR!


Diff:
diff --git a/Clique/README.md b/Clique/README.md
index bca4d19..c47cfb3 100644
--- a/Clique/README.md
+++ b/Clique/README.md
@@ -18,6 +18,9 @@ See the following example for a *3-clique* in a graph of 6 vertices:
 
 From [Wikipedia](https://en.wikipedia.org/wiki/Clique_problem#Definitions)
 
+## Dependencies
+The [print](print/) system generates [graphviz](https://graphviz.org/) files for input, that can be viewed, redirected into a file, or rendered using dot. The latter step can be omitted if graphviz is not present or if graphical output is not desired. 
+
 ## Related
 See also:
 * [independent set](https://github.com/joshuaguerin/Answer-Set-Programming-Algorithms/tree/master/Independent_Set) problem
diff --git a/Clique/k_clique.lp b/Clique/k_clique.lp
index b174e1b..9df2eed 100644
--- a/Clique/k_clique.lp
+++ b/Clique/k_clique.lp
@@ -3,10 +3,13 @@
  * Author: Joshua T. Guerin
  *
  * Description: Given a graph and an integer, n, find an n-clique in the graph.
- * Use: clingo clique.lp instance.lp -c n=<val>
- *      clingo clique.lp instance.lp -c n=<val> -n 0
+ *
+ * Use: clingo k_clique.lp instance.lp -c n=<val>
+ *      clingo k_clique.lp instance.lp -c n=<val> -n 0
+ *          where instance.lp contains node and edge predicates.
  *%
 
+
 % Edges are undirected
 edge(Y, X) :- edge(X, Y).
 
diff --git a/Clique/max_clique.lp b/Clique/max_clique.lp
index 541a19b..3c350dd 100644
--- a/Clique/max_clique.lp
+++ b/Clique/max_clique.lp
@@ -3,8 +3,9 @@
  * Author: Joshua T. Guerin
  *
  * Description: Given a graph and an integer, n, find an n-clique in the graph.
- * Use: clingo clique.lp instance.lp
- *      where instance.lp contains node and edge predicates.
+ *
+ * Use: clingo max_clique.lp instance.lp
+ *          where instance.lp contains node and edge predicates.
  *%
 
 % Edges are undirected
diff --git a/Clique/print/print.py b/Clique/print/print.py
new file mode 100644
index 0000000..ecada46
--- /dev/null
+++ b/Clique/print/print.py
@@ -0,0 +1,75 @@
+# Use:
+# clingo k_clique.lp instance.lp -c n=<val> | python3 print/print.py | dot -Tpdf -o clique.pdf
+# clingo max_clique.lp instance.lp | python3 print/print.py | dot -Tpdf -o clique.pdf
+#
+# Note:
+#  The final pipe and call to dot can be omitted if graphviz is not present.
+#  To be ran in previous directory where instance.lp, k_clique.lp, and max_clique.lp are.
+
+
+import re
+
+
+output = '''// Graph visualization using dot
+strict graph {
+    node [color=black]
+    edge [color=black]
+
+'''
+
+# Reading in graph
+instance = open("instance.lp", "r")
+instance_lines = instance.readlines()
+
+for line in instance_lines:
+    if line.startswith("node"):
+        nums = re.findall(r'\d+', line)
+
+        for n in nums:
+            output += "    " + n + "\n"
+    
+    elif line.startswith("edge"):
+        nums = re.findall(r'\d+', line)
+
+        if len(nums) == 2:
+            output += "    " + nums[0] + " -- " + nums[1] + "\n"
+
+        else:
+            output += "    " + nums[0] + " -- {" + " ".join(nums[1:]) + "}\n"
+
+instance.close()
+
+# Reading in clingo output
+toks_last = None
+toks = None
+toks_next = input().split()
+
+while not (toks_next[0].startswith("SAT") or toks_next[0].startswith("UNSAT") or toks_next[0].startswith("OPT")):
+    toks_last = toks
+    toks = toks_next
+    toks_next = input().split()
+
+if toks_next[0].startswith("OPT"):
+    toks = toks_last
+    toks.pop() # This erases the number from "count(n)"
+
+if toks_next[0].startswith("UNSAT"):
+    print("No solution found.")
+    
+else:
+    output += '''
+    subgraph cluster_0 {
+        style=invis
+
+'''
+    
+    for t in toks:
+        nums = re.findall(r'\d+', t)
+
+        if len(nums) == 1:
+            output += "        " + nums[0] + " [color=red]\n"
+
+        elif len(nums) == 2:
+            output += "        " + nums[0] + " -- " + nums[1] + " [color=red]\n"
+
+    print(output + "    }\n}\n")
diff --git a/Perfect-Numbers/perfect_numbers.lp b/Perfect-Numbers/perfect_numbers.lp
index 1a3484a..d243a0b 100644
--- a/Perfect-Numbers/perfect_numbers.lp
+++ b/Perfect-Numbers/perfect_numbers.lp
@@ -2,9 +2,10 @@
  * File: perfect.lp
  * Author: Joshua T. Guerin
  *
- * Description: Given an integer n, compute primes from 2..n
- * Use: clingo prime.lp -c n=max
- *      where composites between 2..max are to be generated.
+ * Description: Given an integer n, perfect numbers from 2..n
+ *
+ * Use: clingo perfect_numbers.lp -c n=<max>
+ *      where perfect numbers between 2..max are to be generated.
  *%
 
 
@@ -14,4 +15,4 @@ divisor(M, N) :- num(N), num(M), N!=M, N\M==0.
 
 perfect(N) :- num(N), #sum {M : divisor(M, N)} == N.
 
-#show perfect/1.
\ No newline at end of file
+#show perfect/1.
diff --git a/Perfect-Numbers/print/print.py b/Perfect-Numbers/print/print.py
new file mode 100644
index 0000000..5bff3e6
--- /dev/null
+++ b/Perfect-Numbers/print/print.py
@@ -0,0 +1,21 @@
+# Use:
+# python3 print/print.py
+#
+# To be ran in previous directory where perfect_numbers.lp is.
+# notes: These solutions are hard-coded. The clingo program will struggle coming up with all of these.
+
+
+nums = [(6, [1, 2, 3]),
+        (28, [1, 2, 4, 7, 14]),
+        (496, [1, 2, 4, 8, 16, 31, 62, 124, 248]),
+        (8128, [1, 2, 4, 8, 16, 32, 64, 127, 254, 508, 1016, 2032, 4064]),
+        (33550336, [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8191, 16382, 32764, 65528, 131056, 262112, 524224, 1048448, 2096896, 4193792, 8387584, 16775168]),
+        (8589869056, [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131071, 262142, 524284, 1048568, 2097136, 4194272, 8388544, 16777088, 33554176, 67108352, 134216704, 268433408, 536866816, 1073733632, 2147467264, 4294934528]),
+        (137438691328, [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524287, 1048574, 2097148, 4194296, 8388592, 16777184, 33554368, 67108736, 134217472, 268434944, 536869888, 1073739776, 2147479552, 4294959104, 8589918208, 17179836416, 34359672832, 68719345664]),
+        (2305843008139952128, [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 2097152, 4194304, 8388608, 16777216, 33554432, 67108864, 134217728, 268435456, 536870912, 1073741824, 2147483647, 4294967294, 8589934588, 17179869176, 34359738352, 68719476704, 137438953408, 274877906816, 549755813632, 1099511627264, 2199023254528, 4398046509056, 8796093018112, 17592186036224, 35184372072448, 70368744144896, 140737488289792, 281474976579584, 562949953159168, 1125899906318336, 2251799812636672, 4503599625273344, 9007199250546688, 18014398501093376, 36028797002186752, 72057594004373504, 144115188008747008, 288230376017494016, 576460752034988032, 1152921504069976064]),
+        (2658455991569831744654692615953842176, [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 2097152, 4194304, 8388608, 16777216, 33554432, 67108864, 134217728, 268435456, 536870912, 1073741824, 2147483648, 4294967296, 8589934592, 17179869184, 34359738368, 68719476736, 137438953472, 274877906944, 549755813888, 1099511627776, 2199023255552, 4398046511104, 8796093022208, 17592186044416, 35184372088832, 70368744177664, 140737488355328, 281474976710656, 562949953421312, 1125899906842624, 2251799813685248, 4503599627370496, 9007199254740992, 18014398509481984, 36028797018963968, 72057594037927936, 144115188075855872, 288230376151711744, 576460752303423488, 1152921504606846976, 2305843009213693951, 4611686018427387902, 9223372036854775804, 18446744073709551608, 36893488147419103216, 73786976294838206432, 147573952589676412864, 295147905179352825728, 590295810358705651456, 1180591620717411302912, 2361183241434822605824, 4722366482869645211648, 9444732965739290423296, 18889465931478580846592, 37778931862957161693184, 75557863725914323386368, 151115727451828646772736, 302231454903657293545472, 604462909807314587090944, 1208925819614629174181888, 2417851639229258348363776, 4835703278458516696727552, 9671406556917033393455104, 19342813113834066786910208, 38685626227668133573820416, 77371252455336267147640832, 154742504910672534295281664, 309485009821345068590563328, 618970019642690137181126656, 1237940039285380274362253312, 2475880078570760548724506624, 4951760157141521097449013248, 9903520314283042194898026496, 19807040628566084389796052992, 39614081257132168779592105984, 79228162514264337559184211968, 158456325028528675118368423936, 316912650057057350236736847872, 633825300114114700473473695744, 1267650600228229400946947391488, 2535301200456458801893894782976, 5070602400912917603787789565952, 10141204801825835207575579131904, 20282409603651670415151158263808, 40564819207303340830302316527616, 81129638414606681660604633055232, 162259276829213363321209266110464, 324518553658426726642418532220928, 649037107316853453284837064441856, 1298074214633706906569674128883712, 2596148429267413813139348257767424, 5192296858534827626278696515534848, 10384593717069655252557393031069696, 20769187434139310505114786062139392, 41538374868278621010229572124278784, 83076749736557242020459144248557568, 166153499473114484040918288497115136, 332306998946228968081836576994230272, 664613997892457936163673153988460544, 1329227995784915872327346307976921088])
+]
+
+print("Perfect Numbers: ")
+for item in nums:
+    print('%d =\n%s\n' % (item[0], ' + '.join(str(i) for i in item[1])))
diff --git a/Prime-Sieve/composite.lp b/Prime-Sieve/composite.lp
index 78a2b6c..e4f42d6 100644
--- a/Prime-Sieve/composite.lp
+++ b/Prime-Sieve/composite.lp
@@ -3,9 +3,11 @@
  * Author: Joshua T. Guerin
  *
  * Description: Given an integer n, compute composites from 2..n
- * Use: clingo composite.lp -c n=max
+ *
+ * Use: clingo composite.lp -c n=<max>
  *      where composites between 2..max are to be generated.
  *%
+ 
 
 num(2..n).
 
diff --git a/Prime-Sieve/prime.lp b/Prime-Sieve/prime.lp
index c365b3c..a53567d 100644
--- a/Prime-Sieve/prime.lp
+++ b/Prime-Sieve/prime.lp
@@ -3,14 +3,16 @@
  * Author: Joshua T. Guerin
  *
  * Description: Given an integer n, compute primes from 2..n
- * Use: clingo prime.lp -c n=max
- *      where composites between 2..max are to be generated.
+ *
+ * Use: clingo prime.lp -c n=<max>
+ *      where primes between 2..max are to be generated.
  *%
 
+
 num(2..n).
 
 composite(X) :- num(X), num(Y), X\Y==0, X!=Y.
 
 prime(N) :- num(N), not composite(N).
 
-#show prime/1.
\ No newline at end of file
+#show prime/1.
diff --git a/Prime-Sieve/print/print.py b/Prime-Sieve/print/print.py
new file mode 100644
index 0000000..49a3661
--- /dev/null
+++ b/Prime-Sieve/print/print.py
@@ -0,0 +1,29 @@
+# Use:
+# clingo <prime/composite>.lp -c n=<max> | python3 print/print.py
+#
+# To be ran in previous directory where prime.lp and composite.lp are.
+
+
+toks = input().split()
+
+while not toks[0].startswith("Answer:"):
+    toks = input().split()
+
+toks = input().split()
+
+if len(toks) == 0:
+    print("None found.")
+
+else:
+    if toks[0].startswith("prime"):
+        print("Primes found: ", end='')
+        nums = [int(t[6:-1]) for t in toks]
+    else:
+        print("Composites found: ", end='')
+        i = 10
+        nums = [int(t[10:-1]) for t in toks]
+
+    nums.sort()
+        
+    print(", ".join(str(n) for n in nums))
+
diff --git a/Subset-Sum/print/print.py b/Subset-Sum/print/print.py
index b19119c..895a36c 100644
--- a/Subset-Sum/print/print.py
+++ b/Subset-Sum/print/print.py
@@ -1 +1,29 @@
-# Pretty printer goes here
+# Use:
+# clingo subset_sum.lp instance.lp -c n=<val> | python3 print/print.py
+#
+# To be ran in previous directory where subset_sum.lp and instance.lp are.
+
+
+def get_input():
+    toks = input().split()
+
+    while not toks[0].startswith("Answer:"):
+        if toks[0].startswith("UNSATISFIABLE"):
+            return None
+        toks = input().split()
+
+    toks = input().split()
+    return toks
+
+toks = get_input()
+
+# output
+if toks is not None:
+    nums = [int(t[4:-1]) for t in toks]
+
+    nums.sort()
+
+    print(str(sum(nums)) + ' = ' + ' + '.join(str(n) for n in nums))
+
+else:
+    print("No solution found.")


Reviews:
user: login: David-Gonzalez-Sua
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-21T21:21:51Z
user: login: David-Gonzalez-Sua
body: I left a comment on a couple of lines that don't actually change anything in the code that we could avoid merging if necessary. Otherwise, as long as the documentation looks good, I thinks its ready to go.
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-21T21:23:28Z
user: login: David-Gonzalez-Sua
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-21T21:37:44Z
user: login: mads1158
body: Looks good, I'm not as familiar with RegEx, so if someone else can clear those.
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-22T18:01:13Z

Review comments:
diff_hunk: @@ -3,14 +3,16 @@
  * Author: Joshua T. Guerin
  *
  * Description: Given an integer n, compute primes from 2..n
- * Use: clingo prime.lp -c n=max
- *      where composites between 2..max are to be generated.
+ *
+ * Use: clingo prime.lp -c n=<max>
+ *      where primes between 2..max are to be generated.
  *%
 
+
 num(2..n).
 
 composite(X) :- num(X), num(Y), X\Y==0, X!=Y.
 
 prime(N) :- num(N), not composite(N).
 
-#show prime/1.
\ No newline at end of file
+#show prime/1.
path: Prime-Sieve/prime.lp
user: login: David-Gonzalez-Sua
body: No change occurring, so we could avoid merging this and the above line changes.
created_at: 2025-05-21T21:21:51Z
author_association: COLLABORATOR
path: Clique/print/print.py
user: login: David-Gonzalez-Sua
body: I just realized, this will not work if nodes in the instance.lp file are created using something like (1 .. 9)
created_at: 2025-05-21T21:37:44Z
author_association: COLLABORATOR

PR comments:
user: login: mads1158
created_at: 2025-05-22T18:00:01Z
body: Looks good to me! Had to review some regex, so maybe if Dr. Guerin can take a little closer look at those to make sure the statements are solid
author_association: COLLABORATOR
user: login: joshuaguerin
created_at: 2025-05-27T15:28:29Z
body: @gonz9032 Working my way through things now. Clique looks good. For the perfect numbers--I wonder if we could be a bit more explicit, even though it constitutes a bit more work.

Here is what you have:
> Perfect Numbers: 6, 28

What if we did a bit more arithmetic and did something like:
Perfect numbers: 
6 = 1 + 2 + 3
28 = 1 + 2 + 4 + 7 + 14

I'm actually thinking we could do a nasty hack here and just hardcode those that are known, as so few are known(?). I doubt anyone will be using this to discover new ones.
author_association: OWNER
user: login: joshuaguerin
created_at: 2025-05-27T15:31:03Z
body: A quick scan has the others looking good!
author_association: OWNER