ravensat 0.1.1 → 0.2.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a47d909acb01362653613bd0a4df6da1d426303a5e0919987b587fe3adcb77fd
4
- data.tar.gz: 46f323addbd8e5f183eeb8bd81c28ffd0994553bc63a2e99689b991a43d1e508
3
+ metadata.gz: 76d71945818d257f977fde25685896cc7e8c7da0d290f329525beb5d7e5c2ca2
4
+ data.tar.gz: 2c201e0b4815822e40f7b15fe8b1492a532c6665bc456a87c8c6f42227468f38
5
5
  SHA512:
6
- metadata.gz: 31ebd03521bce3df1d2447d9bddba7eef23deced640285f9bbfb79cf790bdc2a4af45b6c299e4ed5c6aa8000a43e4114b4d7045f1e963305175de85728a01cee
7
- data.tar.gz: '097189b8c4d177f9cd8658fa4ff16481616dfb9ca72c65fbe80fbcbb63eaa453db55067312ff152c4fff01e613f4f82c745e7594b6a00861ee2b266c7ea740d1'
6
+ metadata.gz: 4a25a24ca9553e5ea66737f21d5cf9942b9202f106f3c0063d3a4fa84816b665797a761ac2399ef0de374e36bfa29a23c5dccaf7f78f0adfa73cd9e45558e591
7
+ data.tar.gz: 74baa2d28e39e50dca6d280a411cc0fd5727f15dca74ada2ec94eb4a1c1c7d785e686293de0202d0e249c03a78685be3c823917519936d79438740c0bd861b90
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ravensat (0.1.0)
4
+ ravensat (0.1.1)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -1,10 +1,11 @@
1
1
  # Ravensat
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/ravensat`. To experiment with that code, run `bin/console` for an interactive prompt.
3
+ Ravensat is an interface to SAT Solver .
4
+ In order to use Ravensat, you need to install SAT Solver and specify the name of the Solver .
5
+ (If you do not specify SAT Solver, it will use the one bundled in the gem .)
4
6
 
5
- TODO: Delete this and the text above, and describe your gem
6
7
 
7
- ## Installation(Not implemented)
8
+ ## Installation
8
9
 
9
10
  Add this line to your application's Gemfile:
10
11
 
@@ -25,8 +26,8 @@ Or install it yourself as:
25
26
  ```ruby
26
27
  require 'ravensat'
27
28
 
28
- a = Ravensat::PropVar.new
29
- b = Ravensat::PropVar.new
29
+ a = Ravensat::VarNode.new
30
+ b = Ravensat::VarNode.new
30
31
 
31
32
  a.value #=> nil
32
33
  b.value #=> nil
@@ -40,7 +41,7 @@ a.value #=> true
40
41
  b.value #=> true
41
42
  ```
42
43
 
43
- ## Development(Not implemented)
44
+ ## Development
44
45
 
45
46
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
46
47
 
@@ -0,0 +1,48 @@
1
+ require 'bundler/setup'
2
+ require 'ravensat'
3
+ require 'pry'
4
+
5
+ # | ---- | ---- | ---- |
6
+ # | X1 | X2 | X3 |
7
+ # | X4 | X5 | X6 |
8
+ # | X7 | X8 | X9 |
9
+ # | ---- | ---- | ---- |
10
+
11
+ x1 = Array.new(9){ Ravensat::VarNode.new }
12
+ x2 = Array.new(9){ Ravensat::VarNode.new }
13
+ x3 = Array.new(9){ Ravensat::VarNode.new }
14
+ x4 = Array.new(9){ Ravensat::VarNode.new }
15
+ x5 = Array.new(9){ Ravensat::VarNode.new }
16
+ x6 = Array.new(9){ Ravensat::VarNode.new }
17
+ x7 = Array.new(9){ Ravensat::VarNode.new }
18
+ x8 = Array.new(9){ Ravensat::VarNode.new }
19
+ x9 = Array.new(9){ Ravensat::VarNode.new }
20
+ x = [x1, x2, x3, x4, x5, x6, x7, x8, x9]
21
+
22
+ logic = x1[4]
23
+
24
+ # one square, one number
25
+ 9.times do |i|
26
+ logic &= Ravensat::RavenClaw.alo [x[i][0], x[i][1], x[i][2], x[i][3], x[i][4], x[i][5], x[i][6], x[i][7], x[i][8]]
27
+ logic &= Ravensat::RavenClaw.amo [x[i][0], x[i][1], x[i][2], x[i][3], x[i][4], x[i][5], x[i][6], x[i][7], x[i][8]]
28
+ end
29
+
30
+ # x1 ~ x9 are all different numbers
31
+ 9.times do |i|
32
+ logic &= Ravensat::RavenClaw.alo [x[0][i], x[1][i], x[2][i], x[3][i], x[4][i], x[5][i], x[6][i], x[7][i], x[8][i]]
33
+ logic &= Ravensat::RavenClaw.amo [x[0][i], x[1][i], x[2][i], x[3][i], x[4][i], x[5][i], x[6][i], x[7][i], x[8][i]]
34
+ end
35
+
36
+ # TODO: generate constraint that sum to 15 in a row
37
+ # sum to 15 in a row
38
+
39
+ solver = Ravensat::Solver.new
40
+ solver.solve logic
41
+
42
+ puts <<~"MAGIC_SQUARE"
43
+ | --- | --- | --- |
44
+ | #{x1.index{|x| x.value} + 1} | #{x2.index{|x| x.value} + 1} | #{x3.index{|x| x.value} + 1} |
45
+ | #{x4.index{|x| x.value} + 1} | #{x5.index{|x| x.value} + 1} | #{x6.index{|x| x.value} + 1} |
46
+ | #{x7.index{|x| x.value} + 1} | #{x8.index{|x| x.value} + 1} | #{x9.index{|x| x.value} + 1} |
47
+ | --- | --- | --- |
48
+ MAGIC_SQUARE
@@ -0,0 +1,4 @@
1
+ module Ravensat
2
+ class AndNode < OprNode
3
+ end
4
+ end
@@ -0,0 +1,51 @@
1
+ module Ravensat
2
+ class Node
3
+ include Enumerable
4
+
5
+ attr_reader :children
6
+ def initialize
7
+ @children = []
8
+ end
9
+
10
+ def each
11
+ yield(self)
12
+ @children.each do |child|
13
+ child.each {|c| yield(c)}
14
+ end
15
+ end
16
+
17
+ def &(object)
18
+ AndNode.new(self, object)
19
+ end
20
+
21
+ def |(object)
22
+ OrNode.new(self, object)
23
+ end
24
+
25
+ # def tree_text
26
+ # self.each do |child|
27
+ # child.to_s
28
+ # end
29
+ # end
30
+
31
+ def to_s
32
+ self.class.name
33
+ end
34
+
35
+ def cnf?
36
+ @children.map(&:cnf?).reduce(:&)
37
+ end
38
+
39
+ def vars
40
+ self.select{|node| node.is_a? VarNode}.uniq
41
+ end
42
+
43
+ def vars_size
44
+ self.vars.size
45
+ end
46
+
47
+ def clauses_size
48
+ self.count{|node| node.is_a? AndNode} + 1
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,4 @@
1
+ module Ravensat
2
+ class NotNode < OprNode
3
+ end
4
+ end
@@ -0,0 +1,7 @@
1
+ module Ravensat
2
+ class OprNode < Node
3
+ def initialize(*nodes)
4
+ @children = nodes
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,8 @@
1
+ module Ravensat
2
+ class OrNode < OprNode
3
+ def cnf?
4
+ return false if @children.any?{|node| node.is_a? AndNode}
5
+ @children.map(&:cnf?).reduce(:&)
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,17 @@
1
+ module Ravensat
2
+ class VarNode < Node
3
+ attr_accessor :value
4
+ def initialize
5
+ @value
6
+ @children = []
7
+ end
8
+
9
+ def ~@
10
+ NotNode.new(self)
11
+ end
12
+
13
+ def cnf?
14
+ true
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,24 @@
1
+ module Ravensat
2
+ class DimacsDecoder
3
+ def decode(model, name_table)
4
+ inverted_name_table = name_table.invert
5
+ case model.first
6
+ when "SAT"
7
+ model.last.split.each do |e|
8
+ if e == '0'
9
+ next
10
+ elsif e[0] == "-"
11
+ index = e.slice(1..-1)
12
+ inverted_name_table[index].value = false
13
+ else
14
+ index = e
15
+ inverted_name_table[index].value = true
16
+ end
17
+ end
18
+ true
19
+ when "UNSAT"
20
+ false
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,32 @@
1
+ module Ravensat
2
+ class DimacsEncoder
3
+ attr_reader :name_table
4
+ def initialize
5
+ @name_table = {}
6
+ end
7
+
8
+ def to_dimacs(formula)
9
+ return nil unless formula.cnf?
10
+
11
+ dimacs_header = "p cnf #{formula.vars_size} #{formula.clauses_size}"
12
+ dimacs_body = ""
13
+ create_table(formula)
14
+ formula.each do |node|
15
+ case node
16
+ when AndNode
17
+ when OrNode then dimacs_body << "\n"
18
+ when NotNode then dimacs_body << "-"
19
+ when VarNode then dimacs_body << @name_table[node] << " "
20
+ end
21
+ end
22
+
23
+ dimacs_body.gsub!(/\n{2,}/, "\n").gsub!(/\n/, "0\n") << '0'
24
+
25
+ dimacs_header + dimacs_body
26
+ end
27
+
28
+ def create_table(formula)
29
+ @name_table = formula.vars.zip((1..formula.vars.size).map(&:to_s)).to_h
30
+ end
31
+ end
32
+ end
@@ -15,10 +15,11 @@ module Ravensat
15
15
  # end
16
16
 
17
17
  def solve( cnf )
18
+ encoder = DimacsEncoder.new
18
19
  @input_file = Tempfile.open(["ravensat",".cnf"])
19
20
  @output_file = Tempfile.open(["ravensat",".mdl"])
20
21
 
21
- @input_file.write cnf.to_dimacs
22
+ @input_file.write encoder.to_dimacs(cnf)
22
23
  @input_file.flush
23
24
 
24
25
  case @name
@@ -28,18 +29,9 @@ module Ravensat
28
29
  system("#{@name} #{@input_file.to_path} #{@output_file.to_path}")
29
30
  end
30
31
 
32
+ decoder = DimacsDecoder.new
31
33
  model = @output_file.read.split("\n")
32
-
33
- case model.first
34
- when "SAT"
35
- model.last.split.each do |e|
36
- next if e == '0'
37
- cnf.name_table.find{|key,value| value == e.to_i.abs.to_s}.first.value = !e.start_with?('-')
38
- end
39
- Arcteryx::SAT
40
- when "UNSAT"
41
- Arcteryx::UNSAT
42
- end
34
+ decoder.decode(model, encoder.name_table)
43
35
  end
44
36
 
45
37
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ravensat
4
- VERSION = "0.1.1"
4
+ VERSION = "0.2.1"
5
5
  end
data/lib/ravensat.rb CHANGED
@@ -4,12 +4,20 @@ require_relative "ravensat/version"
4
4
 
5
5
  module Ravensat
6
6
  ravensat = File.dirname(__FILE__) + '/ravensat'
7
+ ast = File.dirname(__FILE__) + '/ravensat/ast'
7
8
  arcteryx = File.dirname(__FILE__) + '/arcteryx'
8
9
 
9
10
  autoload :Solver, ravensat + '/solver.rb'
10
- autoload :PropVar, ravensat + '/prop_var.rb'
11
- autoload :PropLogic, ravensat + '/prop_logic.rb'
11
+ autoload :DimacsEncoder, ravensat + '/dimacs_encoder.rb'
12
+ autoload :DimacsDecoder, ravensat + '/dimacs_decoder.rb'
12
13
  autoload :RavenClaw, ravensat + '/ravenclaw.rb'
13
14
 
15
+ autoload :Node, ast + '/node.rb'
16
+ autoload :VarNode, ast + '/var_node.rb'
17
+ autoload :OprNode, ast + '/opr_node.rb'
18
+ autoload :AndNode, ast + '/and_node.rb'
19
+ autoload :OrNode, ast + '/or_node.rb'
20
+ autoload :NotNode, ast + '/not_node.rb'
21
+
14
22
  autoload :Arcteryx, arcteryx + '/arcteryx.rb'
15
23
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ravensat
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - rikuto matsuda
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-12-16 00:00:00.000000000 Z
11
+ date: 2022-05-16 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email:
@@ -27,11 +27,18 @@ files:
27
27
  - Rakefile
28
28
  - bin/console
29
29
  - bin/setup
30
+ - example/magic_square_3x3.rb
30
31
  - lib/arcteryx/arcteryx.rb
31
32
  - lib/arcteryx/cnf.rb
32
33
  - lib/ravensat.rb
33
- - lib/ravensat/prop_logic.rb
34
- - lib/ravensat/prop_var.rb
34
+ - lib/ravensat/ast/and_node.rb
35
+ - lib/ravensat/ast/node.rb
36
+ - lib/ravensat/ast/not_node.rb
37
+ - lib/ravensat/ast/opr_node.rb
38
+ - lib/ravensat/ast/or_node.rb
39
+ - lib/ravensat/ast/var_node.rb
40
+ - lib/ravensat/dimacs_decoder.rb
41
+ - lib/ravensat/dimacs_encoder.rb
35
42
  - lib/ravensat/ravenclaw.rb
36
43
  - lib/ravensat/solver.rb
37
44
  - lib/ravensat/version.rb
@@ -1,92 +0,0 @@
1
- module Ravensat
2
- class PropLogic
3
- attr_reader :formula
4
- attr_reader :name_table
5
-
6
- def initialize( init_formula )
7
- @formula = init_formula
8
- # (a | b) & (~a | b) & (a | ~b) & (~a | ~b)
9
-
10
- # [:and,
11
- # [:or, a, b],
12
- # [:or, [:not, a], b],
13
- # [:or, a, [:not, b]],
14
- # [:or, [:not, a], [:not, b]]
15
- # ]
16
- end
17
-
18
- def to_cnf
19
-
20
- end
21
-
22
- def to_dimacs
23
- cnf_text = String.new
24
- tmp_name_table = @formula.flatten.uniq.reject{|e| e.class == Symbol}
25
- @name_table = tmp_name_table.zip((1..tmp_name_table.size).to_a.map{|e| e.to_s}).to_h
26
-
27
- nr_vars = tmp_name_table.size
28
- nr_clses = @formula.size - 1
29
- cnf_header = 'p cnf ' << nr_vars.to_s << ' ' << nr_clses.to_s << "\n"
30
- # @formula.is_cnf?
31
- @formula.each do |clause|
32
- # next if clause.first != :and
33
- case clause
34
- when Symbol
35
- next
36
- when Array
37
- case clause.first
38
- when :or
39
- clause.each do |literal|
40
- case literal
41
- when Symbol
42
- next
43
- when Ravensat::PropVar
44
- cnf_text << @name_table[literal] << ' '
45
- when Array #&& literal.first == :not
46
- cnf_text << '-' << @name_table[literal.last] << ' '
47
- end
48
- end
49
- when :not
50
- cnf_text << '-' << @name_table[clause.last] << ' '
51
- end
52
- when Ravensat::PropVar
53
- cnf_text << @name_table[clause] << ' '
54
- end
55
- cnf_text << '0' << "\n"
56
- end
57
- cnf_header + cnf_text
58
- end
59
-
60
- def &( object )
61
- if @formula.first == :and
62
- @formula.append object.formula
63
- else
64
- @formula = [:and, @formula, object.formula]
65
- end
66
- self
67
- # Ravensat::PropLogic.new [:and, @formula, object]
68
- # formulaがcnfである前提の実装
69
- # @formula & other_formula
70
- end
71
-
72
- def |( object )
73
- if @formula.first == :or
74
- @formula.append object.formula
75
- else
76
- @formula = [:or, @formula, object.formula]
77
- end
78
- self
79
- # Ravensat::PropLogic.new [:or, @formula, object]
80
- # 木構造に落とすのが難しそう,実装見送り
81
- # @formula | other_formula
82
- end
83
-
84
- # def to_a
85
- # @formula.each do |f|
86
- # if f.class == PropLogic
87
- # f = f.to_a
88
- # end
89
- # end
90
- # end
91
- end
92
- end
@@ -1,45 +0,0 @@
1
- module Ravensat
2
- class PropVar
3
- attr_accessor :value
4
-
5
- def initialize
6
- @value # => true | false | undef
7
- end
8
-
9
- # def +@
10
- # # unuse?
11
- # # return PropLogic object
12
- # 'this is +@ method'
13
- # end
14
-
15
- # def -@
16
- # # return PropLogic object
17
- # 'this is -@ method'
18
- # end
19
-
20
- def ~@
21
- Ravensat::PropLogic.new [:not, self]
22
- # return PropLogic object
23
- end
24
-
25
- def &( object )
26
- Ravensat::PropLogic.new [:and, self, object.formula]
27
- # return PropLogic object
28
- end
29
-
30
- def |( object )
31
- Ravensat::PropLogic.new [:or, self, object.formula]
32
- # return PropLogic object
33
- end
34
-
35
- def formula
36
- # $BIU$1>F?O%a%=%C%I(B
37
- self
38
- end
39
-
40
- # def self
41
- # 'this is self method'
42
- # end
43
-
44
- end
45
- end