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
@@ -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.
|