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
@@ -6,6 +6,7 @@
|
|
6
6
|
|
7
7
|
require 'set'
|
8
8
|
|
9
|
+
require "logic_tools/logictree.rb"
|
9
10
|
|
10
11
|
module LogicTools
|
11
12
|
|
@@ -69,7 +70,7 @@ module LogicTools
|
|
69
70
|
@prime = true # By default assumed prime
|
70
71
|
end
|
71
72
|
|
72
|
-
## Converts to a string
|
73
|
+
## Converts to a string.
|
73
74
|
def to_s # :nodoc:
|
74
75
|
@bits
|
75
76
|
end
|
@@ -84,7 +85,13 @@ module LogicTools
|
|
84
85
|
end
|
85
86
|
|
86
87
|
## Iterates over the bits of the implicant.
|
88
|
+
#
|
89
|
+
# Returns an enumerator if no block given.
|
87
90
|
def each(&blk)
|
91
|
+
# No block given? Returns an enumerator
|
92
|
+
return to_enum(:each) unless block_given?
|
93
|
+
|
94
|
+
# Block given? Applies it on each bit.
|
88
95
|
@bits.each_char(&blk)
|
89
96
|
end
|
90
97
|
|
@@ -212,7 +219,7 @@ module LogicTools
|
|
212
219
|
##
|
213
220
|
# Describes a pseudo variable associated to an implicant.
|
214
221
|
#
|
215
|
-
# Used for the Petrick's method
|
222
|
+
# Used for the Petrick's method.
|
216
223
|
class VarImp < Variable
|
217
224
|
@@base = 0 # The index of the VarImp for building the variable names
|
218
225
|
|
@@ -371,7 +378,7 @@ module LogicTools
|
|
371
378
|
|
372
379
|
# print "Selected prime implicants are: #{selected}\n"
|
373
380
|
# Generate the resulting tree
|
374
|
-
variables = self.
|
381
|
+
variables = self.get_variables()
|
375
382
|
# First generate the prime implicants trees
|
376
383
|
selected.map! do |prime|
|
377
384
|
# Generate the litterals
|
@@ -4,6 +4,7 @@
|
|
4
4
|
|
5
5
|
require 'forwardable'
|
6
6
|
|
7
|
+
|
7
8
|
module LogicTools
|
8
9
|
|
9
10
|
##
|
@@ -76,11 +77,18 @@ module LogicTools
|
|
76
77
|
include Enumerable
|
77
78
|
|
78
79
|
## Gets a array containing the variables of the tree sorted by name.
|
79
|
-
def
|
80
|
-
result = self.
|
80
|
+
def get_variables()
|
81
|
+
result = self.get_variablesRecurse
|
81
82
|
return result.flatten.uniq.sort
|
82
83
|
end
|
83
84
|
|
85
|
+
## Tells if the node is a parent.
|
86
|
+
#
|
87
|
+
# Default: +false+.
|
88
|
+
def is_parent?
|
89
|
+
return false
|
90
|
+
end
|
91
|
+
|
84
92
|
## Gets the operator.
|
85
93
|
#
|
86
94
|
# Default: +nil+ (none).
|
@@ -107,7 +115,7 @@ module LogicTools
|
|
107
115
|
|
108
116
|
# Block given? Apply it.
|
109
117
|
# Get the variables
|
110
|
-
vars = self.
|
118
|
+
vars = self.get_variables
|
111
119
|
# Compute the number of iterations
|
112
120
|
nlines = 2**vars.size
|
113
121
|
# Generates each bit value for the variables and the
|
@@ -209,19 +217,32 @@ module LogicTools
|
|
209
217
|
#
|
210
218
|
# Default: simply duplicate.
|
211
219
|
def flatten_deep
|
220
|
+
# print "flatten_deep #1 with self=#{self}\n"
|
212
221
|
return self.dup
|
213
222
|
end
|
214
223
|
|
224
|
+
## Reduce the node by removing redundancy from it.
|
225
|
+
#
|
226
|
+
# NOTE: this is not the same purpose a Enumerable::reduce.
|
227
|
+
def reduce
|
228
|
+
# By default, no possible reduction.
|
229
|
+
self.clone
|
230
|
+
end
|
231
|
+
|
215
232
|
## Creates a new tree where the current node is distributed over +node+
|
216
233
|
# according to the +dop+ operator.
|
217
234
|
def distribute(dop,node)
|
218
235
|
fop = dop == :and ? :or : :and
|
219
|
-
# print "dop=#{dop}, fop=#{fop}, node.op=#{node.op}\n"
|
220
236
|
if (node.op == dop) then
|
221
237
|
# Same operator: merge self in node
|
222
238
|
return NodeNary.make(dop, self, *node)
|
223
239
|
elsif (node.op == fop) then
|
224
240
|
# Opposite operator: can distribute
|
241
|
+
# result = NodeNary.make(dop)
|
242
|
+
# node.each do |child|
|
243
|
+
# result << NodeNary.make(dop,child,self).flatten
|
244
|
+
# end
|
245
|
+
# return result.flatten
|
225
246
|
nchildren = node.map do |child|
|
226
247
|
NodeNary.make(dop,child,self).flatten
|
227
248
|
end
|
@@ -256,6 +277,18 @@ module LogicTools
|
|
256
277
|
def eql?(val) # :nodoc:
|
257
278
|
self == val
|
258
279
|
end
|
280
|
+
|
281
|
+
## Tells if +self+ includes +tree+.
|
282
|
+
def include?(tree)
|
283
|
+
# By default: equality comparison.
|
284
|
+
return self == tree
|
285
|
+
end
|
286
|
+
|
287
|
+
## Tells if +self+ covers +tree+
|
288
|
+
def cover?(tree)
|
289
|
+
# By default: equality comparison.
|
290
|
+
return self == tree
|
291
|
+
end
|
259
292
|
end
|
260
293
|
|
261
294
|
|
@@ -268,7 +301,8 @@ module LogicTools
|
|
268
301
|
## Creates a node by +value+.
|
269
302
|
def initialize(value) # :nodoc:
|
270
303
|
@value = value
|
271
|
-
@sym = @value.to_s.to_sym
|
304
|
+
# @sym = @value.to_s.to_sym
|
305
|
+
@sym = nil
|
272
306
|
end
|
273
307
|
|
274
308
|
public
|
@@ -276,7 +310,7 @@ module LogicTools
|
|
276
310
|
## Gets the variables, recursively, without postprocessing.
|
277
311
|
#
|
278
312
|
# Returns the variables into sets of arrays with possible doublon
|
279
|
-
def
|
313
|
+
def get_variablesRecurse() # :nodoc:
|
280
314
|
return [ ]
|
281
315
|
end
|
282
316
|
|
@@ -286,19 +320,31 @@ module LogicTools
|
|
286
320
|
return self.eval() == node.eval()
|
287
321
|
end
|
288
322
|
|
323
|
+
# Node::include? is now enough.
|
324
|
+
# ## Tells if the +self+ includes +tree+.
|
325
|
+
# def include?(tree)
|
326
|
+
# return ( tree.is_a?(NodeValue) and self.eval() == tree.eval() )
|
327
|
+
# end
|
328
|
+
|
289
329
|
## Computes the value of the node.
|
290
330
|
def eval
|
291
331
|
return @value
|
292
332
|
end
|
293
333
|
|
334
|
+
## Gets the operator.
|
335
|
+
def op
|
336
|
+
return @value.to_s.to_sym
|
337
|
+
end
|
338
|
+
|
294
339
|
## Converts to a symbol.
|
295
340
|
def to_sym # :nodoc:
|
341
|
+
@sym = @value.to_s.to_sym unless @sym
|
296
342
|
return @sym
|
297
343
|
end
|
298
344
|
|
299
345
|
## Converts to a string.
|
300
346
|
def to_s # :nodoc:
|
301
|
-
return @value
|
347
|
+
return @value ? "1" : "0"
|
302
348
|
end
|
303
349
|
end
|
304
350
|
|
@@ -331,9 +377,23 @@ module LogicTools
|
|
331
377
|
## Creates a node with variable +name+.
|
332
378
|
def initialize(name)
|
333
379
|
@variable = Variable.get(name)
|
334
|
-
@sym = @variable.to_s.to_sym
|
380
|
+
# @sym = @variable.to_s.to_sym
|
381
|
+
@sym = nil
|
382
|
+
end
|
383
|
+
|
384
|
+
## Gets the operator.
|
385
|
+
#
|
386
|
+
# Default: +nil+ (none).
|
387
|
+
def op
|
388
|
+
:variable
|
335
389
|
end
|
336
390
|
|
391
|
+
# Node::include? is now enough.
|
392
|
+
# ## Tells if the +self+ includes +tree+.
|
393
|
+
# def include?(tree)
|
394
|
+
# return ( tree.is_a?(NodeVar) and self.variable == tree.variable )
|
395
|
+
# end
|
396
|
+
|
337
397
|
## Computes the value of the node.
|
338
398
|
def eval()
|
339
399
|
return @variable.value
|
@@ -341,13 +401,14 @@ module LogicTools
|
|
341
401
|
|
342
402
|
## Converts to a symbol.
|
343
403
|
def to_sym # :nodoc:
|
404
|
+
@sym = @variable.to_s.to_sym unless @sym
|
344
405
|
return @sym
|
345
406
|
end
|
346
407
|
|
347
408
|
## Gets the variables, recursively, without postprocessing.
|
348
409
|
#
|
349
410
|
# Returns the variables into sets of arrays with possible doublon
|
350
|
-
def
|
411
|
+
def get_variablesRecurse() # :nodoc:
|
351
412
|
return [ @variable ]
|
352
413
|
end
|
353
414
|
|
@@ -359,7 +420,11 @@ module LogicTools
|
|
359
420
|
|
360
421
|
## Converts to a string.
|
361
422
|
def to_s # :nodoc:
|
362
|
-
|
423
|
+
result = variable.to_s
|
424
|
+
# Variables using more than one character are parenthesized
|
425
|
+
# to avoid confunsion with the AND operator.
|
426
|
+
result = "{" + result + "}" if (result.size > 1)
|
427
|
+
return result
|
363
428
|
end
|
364
429
|
end
|
365
430
|
|
@@ -383,7 +448,8 @@ module LogicTools
|
|
383
448
|
# Children are ok
|
384
449
|
@op = op.to_sym
|
385
450
|
@children = children
|
386
|
-
@sym = self.to_s.to_sym
|
451
|
+
# @sym = self.to_s.to_sym
|
452
|
+
@sym = nil
|
387
453
|
end
|
388
454
|
|
389
455
|
public
|
@@ -399,10 +465,23 @@ module LogicTools
|
|
399
465
|
end
|
400
466
|
end
|
401
467
|
|
468
|
+
## Tells if the node is a parent.
|
469
|
+
def is_parent?
|
470
|
+
return true
|
471
|
+
end
|
402
472
|
|
403
473
|
# Also acts as an array of nodes
|
404
474
|
def_delegators :@children, :[], :empty?, :size
|
405
475
|
|
476
|
+
## Adds a +child+.
|
477
|
+
def add(child)
|
478
|
+
unless child.is_a?(Node) then
|
479
|
+
raise ArgumentError.new("Not a valid class for a child: "+
|
480
|
+
"#{child.class}")
|
481
|
+
end
|
482
|
+
@children << child
|
483
|
+
end
|
484
|
+
|
406
485
|
## Iterates over the children.
|
407
486
|
def each(&blk) # :nodoc:
|
408
487
|
# No block given? Return an enumerator.
|
@@ -434,15 +513,16 @@ module LogicTools
|
|
434
513
|
|
435
514
|
## Converts to a symbol.
|
436
515
|
def to_sym # :nodoc:
|
516
|
+
@sym = self.to_s.to_sym unless @sym
|
437
517
|
return @sym
|
438
518
|
end
|
439
519
|
|
440
520
|
## Gets the variables, recursively, without postprocessing.
|
441
521
|
#
|
442
522
|
# Returns the variables into sets of arrays with possible doublon
|
443
|
-
def
|
523
|
+
def get_variablesRecurse() # :nodoc:
|
444
524
|
return @children.reduce([]) do |res,child|
|
445
|
-
res.concat(child.
|
525
|
+
res.concat(child.get_variablesRecurse)
|
446
526
|
end
|
447
527
|
end
|
448
528
|
|
@@ -450,6 +530,7 @@ module LogicTools
|
|
450
530
|
def ==(node) # :nodoc:
|
451
531
|
return false unless node.is_a?(Node)
|
452
532
|
return false unless self.op == node.op
|
533
|
+
return false unless self.size == node.size
|
453
534
|
# There is no find_with_index!
|
454
535
|
# return ! @children.find_with_index {|child,i| child != node[i] }
|
455
536
|
@children.each_with_index do |child,i|
|
@@ -458,6 +539,46 @@ module LogicTools
|
|
458
539
|
return true
|
459
540
|
end
|
460
541
|
|
542
|
+
## Tells if +self+ includes +tree+.
|
543
|
+
#
|
544
|
+
# NOTE: * This is a tree inclusion, not a logical inclusion.
|
545
|
+
# * It is assumed that the trees are sorted.
|
546
|
+
def include?(tree)
|
547
|
+
# Check from current node.
|
548
|
+
if self.op == tree.op and self.size >= tree.size then
|
549
|
+
return true unless tree.each.with_index.find do |child,i|
|
550
|
+
child != @children[i]
|
551
|
+
end
|
552
|
+
end
|
553
|
+
# Check each child.
|
554
|
+
@children.each do |child|
|
555
|
+
return true if child.include?(tree)
|
556
|
+
end
|
557
|
+
# Do not include.
|
558
|
+
return false
|
559
|
+
end
|
560
|
+
|
561
|
+
## Tells if +self+ covers +tree+.
|
562
|
+
#
|
563
|
+
# NOTE: * It is assumed that the trees are sorted.
|
564
|
+
# * There might still be cover even when the result is false.
|
565
|
+
# For exact cover checking, please use the LogicTools::Cover
|
566
|
+
# class.
|
567
|
+
def cover?(tree)
|
568
|
+
# Different operators, no probable cover.
|
569
|
+
return false if self.op != tree.op
|
570
|
+
# Check for equality with one child.
|
571
|
+
return true unless tree.each.with_index.find do |child,i|
|
572
|
+
child != @children[i]
|
573
|
+
end
|
574
|
+
# Check each child.
|
575
|
+
@children.each do |child|
|
576
|
+
return true if ( child.op == self.op and child.cover?(tree) )
|
577
|
+
end
|
578
|
+
# Do not include.
|
579
|
+
return false
|
580
|
+
end
|
581
|
+
|
461
582
|
# WRONG
|
462
583
|
## Reduce a node: remove its redudancies using absbortion rules
|
463
584
|
# NOTE: NEED to CONSIDER X~X and X+~X CASES
|
@@ -495,28 +616,37 @@ module LogicTools
|
|
495
616
|
def reduce
|
496
617
|
# The operator used for the factors
|
497
618
|
fop = @op == :and ? :or : :and
|
498
|
-
# Gather the terms
|
499
|
-
# comparison
|
619
|
+
# Gather the terms to sorted nodes
|
500
620
|
terms = @children.map do |child|
|
501
|
-
|
502
|
-
[ child, child.sort.to_s ]
|
503
|
-
else
|
504
|
-
[ child, child.to_s ]
|
505
|
-
end
|
621
|
+
child.op == fop ? child.sort : child
|
506
622
|
end
|
507
623
|
nchildren = []
|
508
624
|
# Keep only the terms that do not contain another one
|
625
|
+
# TODO: this loop could be faster I think...
|
509
626
|
terms.each_with_index do |term0,i|
|
510
627
|
skipped = false
|
511
628
|
terms.each_with_index do |term1,j|
|
512
629
|
next if (i==j) # Same term
|
513
|
-
|
514
|
-
|
630
|
+
# Checks the X~X or X+~X cases.
|
631
|
+
if ( term0.op == :not and term0.child == term1 ) or
|
632
|
+
( term1.op == :not and term1.child == term0 ) then
|
633
|
+
# Reduceable to 0 or 1
|
634
|
+
return self.op == :and ? NodeFalse.new : NodeTrue.new
|
635
|
+
end
|
636
|
+
# Checks the covering.
|
637
|
+
next if (term0.op != term1.op) # Different operators
|
638
|
+
# if (term0.include?(term1) and term0 != term1) then
|
639
|
+
# # term0 contains term1 but is different, skip it
|
640
|
+
# skipped = true
|
641
|
+
# break
|
642
|
+
# end
|
643
|
+
if term1.cover?(term0) then
|
644
|
+
# term1 covers term0 skip term0.
|
515
645
|
skipped = true
|
516
|
-
break
|
646
|
+
# break
|
517
647
|
end
|
518
648
|
end
|
519
|
-
nchildren << term0
|
649
|
+
nchildren << term0 unless skipped # Term has not been skipped
|
520
650
|
end
|
521
651
|
# Avoid duplicates
|
522
652
|
nchildren.uniq!
|
@@ -599,7 +729,7 @@ module LogicTools
|
|
599
729
|
|
600
730
|
## Duplicates the node.
|
601
731
|
def dup # :nodoc:
|
602
|
-
return NodeAnd.new(
|
732
|
+
return NodeAnd.new(*@children.map(&:dup))
|
603
733
|
end
|
604
734
|
|
605
735
|
## Computes the value of the node.
|
@@ -613,15 +743,13 @@ module LogicTools
|
|
613
743
|
def to_sum_product(flattened = false) # :nodoc:
|
614
744
|
# Flatten if required
|
615
745
|
node = flattened ? self : self.flatten_deep
|
616
|
-
|
746
|
+
return node unless node.is_parent?
|
617
747
|
# Convert each child to sum of product
|
618
748
|
nchildren = node.map {|child| child.to_sum_product(true) }
|
619
|
-
# print "nchildren = #{nchildren}\n"
|
620
749
|
# Distribute
|
621
750
|
while(nchildren.size>1)
|
622
751
|
dist = []
|
623
752
|
nchildren.each_slice(2) do |left,right|
|
624
|
-
# print "left=#{left}, right=#{right}\n"
|
625
753
|
if right then
|
626
754
|
dist << (left.op == :or ? left.distribute(:and,right) :
|
627
755
|
right.distribute(:and,left))
|
@@ -629,10 +757,8 @@ module LogicTools
|
|
629
757
|
dist << left
|
630
758
|
end
|
631
759
|
end
|
632
|
-
# print "dist=#{dist}\n"
|
633
760
|
nchildren = dist
|
634
761
|
end
|
635
|
-
# print "Distributed nchildren=#{nchildren}\n"
|
636
762
|
# Generate the or
|
637
763
|
if (nchildren.size > 1)
|
638
764
|
return NodeOr.new(*nchildren)
|
@@ -670,7 +796,7 @@ module LogicTools
|
|
670
796
|
|
671
797
|
## Duplicates the node.
|
672
798
|
def dup # :nodoc:
|
673
|
-
return NodeOr.new(
|
799
|
+
return NodeOr.new(*@children.map(&:dup))
|
674
800
|
end
|
675
801
|
|
676
802
|
## Computes the value of the node.
|
@@ -709,7 +835,13 @@ module LogicTools
|
|
709
835
|
end
|
710
836
|
@op = op.to_sym
|
711
837
|
@child = child
|
712
|
-
@sym = self.to_s.to_sym
|
838
|
+
# @sym = self.to_s.to_sym
|
839
|
+
@sym = nil
|
840
|
+
end
|
841
|
+
|
842
|
+
## Tells if the node is a parent.
|
843
|
+
def is_parent?
|
844
|
+
return true
|
713
845
|
end
|
714
846
|
|
715
847
|
## Gets the number of children.
|
@@ -731,8 +863,8 @@ module LogicTools
|
|
731
863
|
## Gets the variables, recursively, without postprocessing.
|
732
864
|
#
|
733
865
|
# Returns the variables into sets of arrays with possible doublon
|
734
|
-
def
|
735
|
-
return @child.
|
866
|
+
def get_variablesRecurse() # :nodoc:
|
867
|
+
return @child.get_variablesRecurse
|
736
868
|
end
|
737
869
|
|
738
870
|
## Iterates over the children.
|
@@ -747,12 +879,22 @@ module LogicTools
|
|
747
879
|
## Compares with +node+.
|
748
880
|
def ==(node) # :nodoc:
|
749
881
|
return false unless node.is_a?(Node)
|
750
|
-
return false unless self.op ==
|
882
|
+
return false unless self.op == node.op
|
751
883
|
return self.child == node.child
|
752
884
|
end
|
753
885
|
|
886
|
+
## Tells if the +self+ includes +tree+.
|
887
|
+
def include?(tree)
|
888
|
+
return true if self == tree # Same tree, so inclusion.
|
889
|
+
# Check the child
|
890
|
+
return true if @child.include?(tree)
|
891
|
+
# Do not include
|
892
|
+
return false
|
893
|
+
end
|
894
|
+
|
754
895
|
## Converts to a symbol.
|
755
896
|
def to_sym # :nodoc:
|
897
|
+
@sym = self.to_s.to_sym unless @sym
|
756
898
|
return @sym
|
757
899
|
end
|
758
900
|
end
|
@@ -805,7 +947,29 @@ module LogicTools
|
|
805
947
|
#
|
806
948
|
# Argument +flattened+ tells if the tree is already flattend
|
807
949
|
def to_sum_product(flattened = false) # :nodoc:
|
808
|
-
return NodeNot.new(@child.to_sum_product(flatten))
|
950
|
+
# return NodeNot.new(@child.to_sum_product(flatten))
|
951
|
+
# Flatten deeply if required.
|
952
|
+
nnode = flattened ? self : self.flatten_deep
|
953
|
+
if (nnode.op != :not) then
|
954
|
+
# Not a NOT any longer.
|
955
|
+
return nnode.to_sum_product
|
956
|
+
end
|
957
|
+
# Still a NOT, so apply De Morgan's law.
|
958
|
+
child = nnode.child
|
959
|
+
if child.op == :or then
|
960
|
+
# Can apply De Morgan's law for OR.
|
961
|
+
return NodeAnd.new( *child.each.map do |n|
|
962
|
+
NodeNot.new(n).to_sum_product
|
963
|
+
end ).to_sum_product
|
964
|
+
elsif child.op == :and then
|
965
|
+
# Can apply De Morgan's law for AND.
|
966
|
+
return NodeOr.new( *child.each.map do |n|
|
967
|
+
NodeNot.new(n).to_sum_product
|
968
|
+
end )
|
969
|
+
else
|
970
|
+
# Nothing to do more.
|
971
|
+
return nnode
|
972
|
+
end
|
809
973
|
end
|
810
974
|
|
811
975
|
## Converts to a string.
|