logic_tools 0.2.4 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|