logic_tools 0.2.4 → 0.3.0
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.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/exe/is_tautology +12 -0
- data/exe/simplify_es +12 -0
- data/lib/logic_tools/is_tautology.rb +46 -0
- data/lib/logic_tools/logicconvert.rb +142 -0
- data/lib/logic_tools/logiccover.rb +828 -0
- data/lib/logic_tools/logicgenerator.rb +222 -0
- data/lib/logic_tools/logicparse.rb +9 -2
- data/lib/logic_tools/logicsimplify_es.rb +734 -0
- data/lib/logic_tools/{logicsimplify.rb → logicsimplify_qm.rb} +10 -3
- data/lib/logic_tools/logictree.rb +200 -36
- data/lib/logic_tools/minimal_column_covers.rb +481 -0
- data/lib/logic_tools/simplify_es.rb +39 -0
- data/lib/logic_tools/simplify_qm.rb +2 -18
- data/lib/logic_tools/test_logic_tools.rb +161 -0
- data/lib/logic_tools/traces.rb +116 -0
- data/lib/logic_tools/truth_tbl.rb +2 -2
- data/lib/logic_tools/version.rb +1 -1
- metadata +16 -3
@@ -0,0 +1,39 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#########################################################################
|
3
|
+
# Simplifies a logic expression using the ESPRESSO algorithm #
|
4
|
+
#########################################################################
|
5
|
+
|
6
|
+
|
7
|
+
# For building logic trees
|
8
|
+
require "logic_tools/logictree.rb"
|
9
|
+
|
10
|
+
# For parsing the inputs
|
11
|
+
require "logic_tools/logicparse.rb"
|
12
|
+
|
13
|
+
# For simplifying with the ESPRESSO method.
|
14
|
+
require "logic_tools/logicsimplify_es.rb"
|
15
|
+
|
16
|
+
# For the command line interface
|
17
|
+
require "logic_tools/logicinput.rb"
|
18
|
+
|
19
|
+
include LogicTools
|
20
|
+
|
21
|
+
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
############################
|
26
|
+
# The main program
|
27
|
+
|
28
|
+
# Iterrate on each expression
|
29
|
+
each_input do |expr|
|
30
|
+
# Parse the expression
|
31
|
+
parsed = string2logic(expr)
|
32
|
+
|
33
|
+
# Simplify it
|
34
|
+
simple = parsed.simplify
|
35
|
+
# print "Computation done\n"
|
36
|
+
|
37
|
+
# Display the result
|
38
|
+
print simple.to_s, "\n"
|
39
|
+
end
|
@@ -10,11 +10,8 @@ require "logic_tools/logictree.rb"
|
|
10
10
|
# For parsing the inputs
|
11
11
|
require "logic_tools/logicparse.rb"
|
12
12
|
|
13
|
-
# For simplifying
|
14
|
-
require "logic_tools/
|
15
|
-
|
16
|
-
# For the command line interface
|
17
|
-
require "logic_tools/logicinput.rb"
|
13
|
+
# For simplifying with the Quine Mc Cluskey method.
|
14
|
+
require "logic_tools/logicsimplify_qm.rb"
|
18
15
|
|
19
16
|
# For the command line interface
|
20
17
|
require "logic_tools/logicinput.rb"
|
@@ -28,19 +25,6 @@ include LogicTools
|
|
28
25
|
############################
|
29
26
|
# The main program
|
30
27
|
|
31
|
-
## Now use the common command line interface
|
32
|
-
# # First gets the expression to treat
|
33
|
-
# $expr = nil
|
34
|
-
# # Is it in the arguments?
|
35
|
-
# unless $*.empty? then
|
36
|
-
# # Yes, get the expression from them
|
37
|
-
# $expr = $*.join
|
38
|
-
# else
|
39
|
-
# # Get the expression from standard input
|
40
|
-
# print "Please enter your expression and end with ^D:\n"
|
41
|
-
# $expr = ARGF.read
|
42
|
-
# end
|
43
|
-
#
|
44
28
|
|
45
29
|
# Iterrate on each expression
|
46
30
|
each_input do |expr|
|
@@ -0,0 +1,161 @@
|
|
1
|
+
######################################################################
|
2
|
+
# The test program for logic tools #
|
3
|
+
# #
|
4
|
+
# NOTE: This still a very imcomplete work! #
|
5
|
+
######################################################################
|
6
|
+
|
7
|
+
# require 'minitest/autorun'
|
8
|
+
require "logic_tools/logicsimplify_es.rb"
|
9
|
+
require "logic_tools/logicgenerator.rb"
|
10
|
+
|
11
|
+
include LogicTools
|
12
|
+
|
13
|
+
## Class for testing the implementation of the ESPRESSO algorithm.
|
14
|
+
class TestEspresso # < MiniTest::Unit::TestCase
|
15
|
+
|
16
|
+
## Creates the tester with a +seed+ for random generation, a
|
17
|
+
# +deadline+ for the simplify steps and a +volume+ before splitting
|
18
|
+
# the cover.
|
19
|
+
def initialize(seed = 0, deadline = Float::INFINITY,
|
20
|
+
volume = Float::INFINITY)
|
21
|
+
@seed = seed
|
22
|
+
@deadline = deadline
|
23
|
+
@volume = volume
|
24
|
+
end
|
25
|
+
|
26
|
+
## Checks if a +cover+ is a tautology by generating its truth table.
|
27
|
+
def truth_tautology(cover)
|
28
|
+
## Generate each possible input and test it on the cover.
|
29
|
+
(2**(cover.width)).times do |i|
|
30
|
+
return false unless cover.eval(i)
|
31
|
+
end
|
32
|
+
return true
|
33
|
+
end
|
34
|
+
|
35
|
+
## Tests the tautology check on covers generated by a random +generator+.
|
36
|
+
#
|
37
|
+
# NOTE: ends when a tautology is actually found.
|
38
|
+
def test_tautology_random(generator)
|
39
|
+
# Create the cover.
|
40
|
+
cover = Cover.new(*generator.each_variable)
|
41
|
+
# Tautology results.
|
42
|
+
taut0, taut1 = false, false
|
43
|
+
# Add random cubes to the cover until a tautology is met while.
|
44
|
+
begin
|
45
|
+
# Add a random cube.
|
46
|
+
cover << generator.random_cube
|
47
|
+
print "Tautology check on cover=[#{cover.to_s}]...\n"
|
48
|
+
# Check if the cover is a tautology using the standard approach.
|
49
|
+
taut0 = cover.is_tautology?
|
50
|
+
print "Through is_tautology?: #{taut0}; "
|
51
|
+
# Check it again with a truth table.
|
52
|
+
taut1 = truth_tautology(cover)
|
53
|
+
print "through truth table: #{taut1}\n"
|
54
|
+
raise "Test failure" unless taut0 == taut1
|
55
|
+
end while (!taut0)
|
56
|
+
return true
|
57
|
+
end
|
58
|
+
|
59
|
+
## Tests randomly +number+ tautology cases on a +dimensions+ boolean
|
60
|
+
# space.
|
61
|
+
def test_tautologies_random(number = 1024, dimensions = 4)
|
62
|
+
# Create the variables.
|
63
|
+
base = "`"
|
64
|
+
variables = dimensions.times.map { |i| base.next!.clone }
|
65
|
+
# Create the generator.
|
66
|
+
generator = Generator.new(*variables)
|
67
|
+
generator.seed = @seed
|
68
|
+
# Performs the tests.
|
69
|
+
number.times do |i|
|
70
|
+
print "Test #{i}: "
|
71
|
+
return false unless test_tautology_random(generator)
|
72
|
+
end
|
73
|
+
return true
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
## Checks if covers have the same truth table.
|
78
|
+
def same_truth_table?(cover0,cover1)
|
79
|
+
return false unless cover0.width == cover1.width
|
80
|
+
# Check for each entry.
|
81
|
+
(2**cover0.width).times do |i|
|
82
|
+
return false unless cover0.eval(i) == cover1.eval(i)
|
83
|
+
end
|
84
|
+
return true
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
## Tests espresso on a given +cover+.
|
89
|
+
def test_espresso(cover)
|
90
|
+
print "ESPRESSO on cover=[#{cover.to_s}]...\n"
|
91
|
+
simple = cover.simplify(@deadline,@volume)
|
92
|
+
print "result: [#{simple}]\n"
|
93
|
+
check0 = (cover + simple.complement).is_tautology?
|
94
|
+
# check0 = same_truth_table?(cover,simple)
|
95
|
+
# assert_equal(true,check0)
|
96
|
+
print "check 0 = #{check0}\n"
|
97
|
+
raise "Test failure" unless check0
|
98
|
+
check1 = (cover.complement + simple).is_tautology?
|
99
|
+
# assert_equal(true,check1)
|
100
|
+
print "check 1 = #{check1}\n"
|
101
|
+
raise "Test failure" unless check1
|
102
|
+
return true
|
103
|
+
end
|
104
|
+
|
105
|
+
## Tests the implementation of the espresso algorithm on each
|
106
|
+
# possible 1-cube cover of 4 variables.
|
107
|
+
#
|
108
|
+
# Test only on cover if a +test+ number is given.
|
109
|
+
def test_espresso_all(test = nil)
|
110
|
+
generator = Generator.new("a","b","c","d")
|
111
|
+
generator.seed = @seed
|
112
|
+
if test then
|
113
|
+
test = test.to_i
|
114
|
+
print "Test #{test}: "
|
115
|
+
return test_espresso(generator.make_1cover(test))
|
116
|
+
else
|
117
|
+
generator.each_1cover.with_index do |cover,i|
|
118
|
+
print "Test #{i}: "
|
119
|
+
return false unless test_espresso(cover)
|
120
|
+
end
|
121
|
+
return true
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
## Tests ESPRESSO on a cover generated by a random +generator+.
|
126
|
+
def test_espresso_random(generator)
|
127
|
+
# Genrate a random cover.
|
128
|
+
cover = generator.random_cover
|
129
|
+
# Test it.
|
130
|
+
return false unless test_espresso(cover)
|
131
|
+
return true
|
132
|
+
end
|
133
|
+
|
134
|
+
## Tests ESPRESSO on randomly +number+ cover cases on a +dimensions+ boolean
|
135
|
+
# space, including at most +max+ cubes.
|
136
|
+
def test_espressos_random(number = 1024, dimensions = 4, max = nil)
|
137
|
+
# Create the variables.
|
138
|
+
base = "`"
|
139
|
+
variables = dimensions.times.map { |i| base.next!.clone }
|
140
|
+
# Create the generator.
|
141
|
+
generator = Generator.new(*variables)
|
142
|
+
generator.seed = @seed
|
143
|
+
generator.max = max if max
|
144
|
+
# Ensures a rate of "-" large enough to have a high probability of
|
145
|
+
# an interesting cover (i.e., which is actually simplifiable).
|
146
|
+
# This rate +r+ is obainted as the solution of the followings:
|
147
|
+
# max/2 * 2**(r*dimensions) >= 2**dimensions
|
148
|
+
# NOTE: max/2 is the average size of the cover.
|
149
|
+
generator.rate = Math::log2(2**(dimensions+1)/max.to_f)/dimensions
|
150
|
+
# Ensures the rate is not too small though.
|
151
|
+
generator.rate = 0.3 unless generator.rate >= 0.3
|
152
|
+
print "rate=#{generator.rate}\n"
|
153
|
+
# Performs the tests.
|
154
|
+
number.times do |i|
|
155
|
+
print "Test #{i}: "
|
156
|
+
return false unless test_espresso_random(generator)
|
157
|
+
end
|
158
|
+
return true
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'logger.rb'
|
2
|
+
|
3
|
+
|
4
|
+
module LogicTools
|
5
|
+
|
6
|
+
## Small class for indenting
|
7
|
+
class Indenter
|
8
|
+
## Creates a new indenter.
|
9
|
+
def initialize
|
10
|
+
@indent = 0
|
11
|
+
end
|
12
|
+
|
13
|
+
## Increase the indent level by +value+.
|
14
|
+
#
|
15
|
+
# NOTE:
|
16
|
+
# * the indent level cannot be bellow 0.
|
17
|
+
# * the value can be negative.
|
18
|
+
def inc(value = 1)
|
19
|
+
@indent += value.to_i
|
20
|
+
@indent = 0 if @indent < 0
|
21
|
+
end
|
22
|
+
|
23
|
+
## Decreases the indent level by +value+.
|
24
|
+
#
|
25
|
+
# NOTE:
|
26
|
+
# * the indent level cannot be bellow 0.
|
27
|
+
# * the value can be negative.
|
28
|
+
def dec(value = 1)
|
29
|
+
@indent -= value.to_i
|
30
|
+
@indent = 0 if @indent < 0
|
31
|
+
end
|
32
|
+
|
33
|
+
## Converts to a string (generates the indent.)
|
34
|
+
def to_s
|
35
|
+
return " " * @indent
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
module Traces
|
41
|
+
|
42
|
+
# Add traces support to the logic tools.
|
43
|
+
|
44
|
+
|
45
|
+
## The logger used for displaying the traces.
|
46
|
+
TRACES = Logger.new(STDOUT)
|
47
|
+
|
48
|
+
## The indent for the traces.
|
49
|
+
TRACES_INDENT = Indenter.new
|
50
|
+
|
51
|
+
# Format the traces
|
52
|
+
TRACES.formatter = proc do |severity, datetime, progname, msg|
|
53
|
+
"[#{severity}] #{datetime}: #{TRACES_INDENT.to_s}#{msg}\n"
|
54
|
+
end
|
55
|
+
TRACES.datetime_format = '%H:%M:%S'
|
56
|
+
|
57
|
+
# By default the trace level is set warn.
|
58
|
+
TRACES.level = Logger::WARN
|
59
|
+
|
60
|
+
|
61
|
+
## Sets the trace level to error.
|
62
|
+
def traces_error
|
63
|
+
TRACES.level = Logger::ERROR
|
64
|
+
end
|
65
|
+
|
66
|
+
## Sets the trace level to warn.
|
67
|
+
def traces_warn
|
68
|
+
TRACES.level = Logger::WARN
|
69
|
+
end
|
70
|
+
|
71
|
+
## Sets the trace level to info.
|
72
|
+
def traces_info
|
73
|
+
TRACES.level = Logger::INFO
|
74
|
+
end
|
75
|
+
|
76
|
+
## Sets the trace level to debug
|
77
|
+
def traces_debug
|
78
|
+
TRACES.level = Logger::DEBUG
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
## Sends an error-level trace.
|
83
|
+
def error(&blk)
|
84
|
+
TRACES.error(&blk)
|
85
|
+
end
|
86
|
+
|
87
|
+
## Sends a warn-level trace.
|
88
|
+
def warn(&blk)
|
89
|
+
TRACES.warn(&blk)
|
90
|
+
end
|
91
|
+
|
92
|
+
## Sends an info-level trace.
|
93
|
+
def info(&blk)
|
94
|
+
TRACES.info(&blk)
|
95
|
+
end
|
96
|
+
|
97
|
+
## Sends a debug-level trace.
|
98
|
+
def debug(&blk)
|
99
|
+
TRACES.debug(&blk)
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
## Increases the indent level by +value+.
|
104
|
+
def inc_indent(value = 1)
|
105
|
+
TRACES_INDENT.inc(value)
|
106
|
+
end
|
107
|
+
|
108
|
+
## Deacreases the indent level by +value+.
|
109
|
+
def dec_indent(value = 1)
|
110
|
+
TRACES_INDENT.dec(value)
|
111
|
+
end
|
112
|
+
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
@@ -39,7 +39,7 @@ include LogicTools
|
|
39
39
|
# $parsed = string2logic($expr)
|
40
40
|
#
|
41
41
|
# # Display the variables
|
42
|
-
# $vars = $parsed.
|
42
|
+
# $vars = $parsed.get_variables
|
43
43
|
# $vars.each { |var| print "#{var} " }
|
44
44
|
# print "\n"
|
45
45
|
#
|
@@ -55,7 +55,7 @@ each_input do |expr|
|
|
55
55
|
parsed = string2logic(expr)
|
56
56
|
|
57
57
|
# Display the variables
|
58
|
-
vars = parsed.
|
58
|
+
vars = parsed.get_variables
|
59
59
|
vars.each { |var| print "#{var} " }
|
60
60
|
print "\n"
|
61
61
|
|
data/lib/logic_tools/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: logic_tools
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lovic Gauthier
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-01-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -74,6 +74,8 @@ description: "LogicTools is a set of command-line tools for processing logic exp
|
|
74
74
|
email:
|
75
75
|
- lovic@ariake-nct.ac.jp
|
76
76
|
executables:
|
77
|
+
- is_tautology
|
78
|
+
- simplify_es
|
77
79
|
- simplify_qm
|
78
80
|
- std_conj
|
79
81
|
- std_dij
|
@@ -89,19 +91,30 @@ files:
|
|
89
91
|
- Rakefile
|
90
92
|
- bin/console
|
91
93
|
- bin/setup
|
94
|
+
- exe/is_tautology
|
95
|
+
- exe/simplify_es
|
92
96
|
- exe/simplify_qm
|
93
97
|
- exe/std_conj
|
94
98
|
- exe/std_dij
|
95
99
|
- exe/truth_tbl
|
96
100
|
- lib/logic_tools.rb
|
101
|
+
- lib/logic_tools/is_tautology.rb
|
102
|
+
- lib/logic_tools/logicconvert.rb
|
103
|
+
- lib/logic_tools/logiccover.rb
|
104
|
+
- lib/logic_tools/logicgenerator.rb
|
97
105
|
- lib/logic_tools/logicinput.rb
|
98
106
|
- lib/logic_tools/logicparse.rb
|
99
|
-
- lib/logic_tools/
|
107
|
+
- lib/logic_tools/logicsimplify_es.rb
|
108
|
+
- lib/logic_tools/logicsimplify_qm.rb
|
100
109
|
- lib/logic_tools/logictree.rb
|
110
|
+
- lib/logic_tools/minimal_column_covers.rb
|
101
111
|
- lib/logic_tools/simplify_bug.txt
|
112
|
+
- lib/logic_tools/simplify_es.rb
|
102
113
|
- lib/logic_tools/simplify_qm.rb
|
103
114
|
- lib/logic_tools/std_conj.rb
|
104
115
|
- lib/logic_tools/std_dij.rb
|
116
|
+
- lib/logic_tools/test_logic_tools.rb
|
117
|
+
- lib/logic_tools/traces.rb
|
105
118
|
- lib/logic_tools/truth_tbl.rb
|
106
119
|
- lib/logic_tools/version.rb
|
107
120
|
- logic_tools.gemspec
|