Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
pro
ndl
Commits
e4920ba8
Verified
Commit
e4920ba8
authored
Apr 07, 2018
by
Marcin Moskal
Browse files
Multivariable unification
parent
dfeaa053
Changes
8
Hide whitespace changes
Inline
Side-by-side
src/main/scala/pl/edu/agh/kis/eis/KnowledgeBase.scala
View file @
e4920ba8
package
pl.edu.agh.kis.eis
class
KnowledgeBase
[
A
](
facts
:
Seq
[
MonoFact
[
A
]])
{
def
querySingle
(
f
:
(
Variable
[
A
]
=>
Seq
[
Placeholder
[
A
]]))
:
Option
[
A
]
=
{
val
variable
=
Variable
[
A
]()
val
placeholders
:
Seq
[
Placeholder
[
A
]]
=
f
(
variable
)
class
KnowledgeBase
[
A
](
val
facts
:
Seq
[
MonoFact
[
A
]])
{
def
queryFirst
(
f
:
(
Variable
[
A
]
=>
Seq
[
Placeholder
[
A
]]))
:
Option
[
A
]
=
{
val
variable
=
Variable
[
A
](
name
=
Some
(
"x"
))
queryDeterministic
(
f
(
variable
)).
headOption
.
flatMap
(
_
.
headOption
).
flatMap
(
_
.
binding
)
}
def
queryDeterministic
(
placeholder
:
Placeholder
[
A
],
knownBindings
:
Seq
[
Set
[
Variable
[
A
]]]
=
Seq
.
empty
)
:
Seq
[
Set
[
Variable
[
A
]]]
=
{
val
variables
=
placeholder
.
variables
val
candidates
=
withArity
(
placeholder
.
arity
).
withMatchingConstants
(
placeholder
.
constants
.
toMap
)
placeholders
.
find
(!
_
.
isBound
).
flatMap
{
p
=>
facts
.
takeWhile
{
fact
=>
p
.
unify
(
fact
)
!
facts
.
containsSlice
(
placeholders
.
map
(
_
.
buildFact
()))
candidates
.
facts
.
map
{
fact
=>
val
unified
=
placeholder
.
unify
(
fact
)
if
(
unified
.
nonEmpty
)
{
variables
.
map
(
x
=>
Variable
(
x
.
binding
,
x
.
name
))
// clones
}
else
{
Set
.
empty
[
Variable
[
A
]]
}
variable
.
binding
}
}.
filter
(
_
.
nonEmpty
)
}
def
queryDeterministic
(
placeholders
:
Seq
[
Placeholder
[
A
]])
:
Seq
[
Set
[
Variable
[
A
]]]
=
placeholders
.
foldLeft
(
Seq
.
empty
[
Set
[
Variable
[
A
]]])
{
case
(
bindings
,
placeholder
)
=>
queryDeterministic
(
placeholder
,
bindings
).
flatMap
(
Variable
.
augmentBindings
(
bindings
,
_
))
}
def
queryNondeterministic
(
f
:
(
Variable
[
A
]
=>
Seq
[
Placeholder
[
A
]]))
:
Set
[
A
]
=
???
def
withArity
(
arity
:
Int
)
:
KnowledgeBase
[
A
]
=
new
KnowledgeBase
[
A
](
facts
.
filter
(
_
.
arity
==
arity
))
def
withMatchingConstants
(
mappings
:
Map
[
Int
,
A
])
:
KnowledgeBase
[
A
]
=
new
KnowledgeBase
(
facts
.
filter
(
_
.
matchesConstants
(
mappings
)))
}
src/main/scala/pl/edu/agh/kis/eis/MonoFact.scala
View file @
e4920ba8
package
pl.edu.agh.kis.eis
case
class
MonoFact
[
A
](
values
:
A*
)
import
scala.util.
{
Success
,
Try
}
case
class
MonoFact
[
A
](
values
:
A*
)
{
def
arity
:
Int
=
values
.
size
def
matchesConstants
(
mappings
:
Map
[
Int
,
A
])
:
Boolean
=
mappings
.
forall
{
case
(
index
,
constant
)
=>
Try
(
values
.
apply
(
index
))
==
Success
(
constant
)
// FIXME Seq is inefficient repr (multiple scans)
}
}
src/main/scala/pl/edu/agh/kis/eis/Placeholder.scala
View file @
e4920ba8
...
...
@@ -7,12 +7,12 @@ case class Placeholder[A](s: VarOrAtom[A]*) {
})
def
buildFact
()
:
MonoFact
[
A
]
=
MonoFact
(
s
.
map
{
case
Variable
(
binding
)
=>
binding
.
get
case
v
:
Variable
[
A
]
=>
v
.
binding
.
get
case
Atom
(
a
)
=>
a
}
:
_
*
)
def
unify
(
fact
:
MonoFact
[
A
])
:
Seq
[
A
]
=
{
val
placeholderVars
=
s
.
zipWithIndex
.
collect
{
case
(
v
@
Variable
(
_
)
,
index
)
=>
(
v
,
index
)
}.
toSet
val
placeholderVars
=
s
.
zipWithIndex
.
collect
{
case
(
v
:
Variable
[
A
]
,
index
:
Int
)
=>
(
v
,
index
)
}.
toSet
placeholderVars
.
foreach
{
case
(
variable
,
index
)
=>
variable
.
bind
(
fact
.
values
(
index
))
}
if
(
buildFact
()
==
fact
)
{
...
...
@@ -21,14 +21,13 @@ case class Placeholder[A](s: VarOrAtom[A]*) {
Seq
.
empty
}
}
}
sealed
trait
VarOrAtom
[
A
]
case
class
Variable
[
A
](
var
binding
:
Option
[
A
]
=
None
)
extends
VarOrAtom
[
A
]
{
def
isBound
:
Boolean
=
binding
.
isDefined
def
bind
(
value
:
A
)
:
A
=
{
binding
=
Some
(
value
)
value
def
isBindingCompatible
(
binding
:
Set
[
Variable
[
A
]])
:
Boolean
=
{
variables
.
filter
(
v
=>
v
.
isBound
&&
binding
.
exists
(
_
.
name
==
v
.
name
)).
forall
(
binding
.
contains
)
}
lazy
val
arity
:
Int
=
s
.
size
// FIXME common with MonoFact
lazy
val
constants
:
Seq
[(
Int
,
A
)]
=
s
.
zipWithIndex
.
collect
{
case
(
Atom
(
const
),
index
)
=>
(
index
,
const
)
}
lazy
val
variables
:
Set
[
Variable
[
A
]]
=
s
.
collect
{
case
v
:
Variable
[
A
]
=>
v
}.
toSet
}
case
class
Atom
[
A
](
a
:
A
)
extends
VarOrAtom
[
A
]
src/main/scala/pl/edu/agh/kis/eis/VarOrAtom.scala
0 → 100644
View file @
e4920ba8
package
pl.edu.agh.kis.eis
sealed
trait
VarOrAtom
[
A
]
case
class
Variable
[
A
](
var
binding
:
Option
[
A
]
=
None
,
name
:
Option
[
String
]
=
None
)
extends
VarOrAtom
[
A
]
{
def
isBound
:
Boolean
=
binding
.
isDefined
def
bind
(
value
:
A
)
:
A
=
{
binding
=
Some
(
value
)
value
}
}
object
Variable
{
def
apply
[
A
](
value
:
A
)
:
Variable
[
A
]
=
Variable
(
Some
(
value
))
def
apply
[
A
](
name
:
String
)
:
Variable
[
A
]
=
Variable
(
name
=
Some
(
name
))
def
apply
[
A
](
name
:
String
,
value
:
A
)
:
Variable
[
A
]
=
Variable
(
Some
(
value
),
Some
(
name
))
def
augmentBindings
[
A
](
knownBindings
:
Seq
[
Set
[
Variable
[
A
]]],
candidate
:
Set
[
Variable
[
A
]])
:
Seq
[
Set
[
Variable
[
A
]]]
=
{
if
(
knownBindings
.
isEmpty
)
{
return
Seq
(
candidate
)
}
val
compatible
=
knownBindings
.
filter
{
knownBinding
=>
val
shared
=
knownBinding
.
filter
(
x
=>
candidate
.
exists
(
_
.
name
==
x
.
name
))
shared
.
forall
(
candidate
.
contains
)
}
compatible
.
map
(
_
++
candidate
)
}
}
case
class
Atom
[
A
](
a
:
A
)
extends
VarOrAtom
[
A
]
\ No newline at end of file
src/test/scala/pl/edu/agh/kis/eis/KnowledgeBaseSpec.scala
View file @
e4920ba8
...
...
@@ -3,31 +3,92 @@ package pl.edu.agh.kis.eis
import
org.scalatest.
{
FlatSpec
,
Matchers
}
class
KnowledgeBaseSpec
extends
FlatSpec
with
Matchers
{
"
KnowledgeBase"
should
"contain facts
"
in
{
"
First-match variable binding"
should
"be returned in
"
in
{
val
facts
=
Seq
(
MonoFact
(
1
),
MonoFact
(
2
),
MonoFact
(
1
,
2
)
MonoFact
(
1
),
MonoFact
(
1
,
2
),
MonoFact
(
2
,
1
)
)
val
kb
=
new
KnowledgeBase
(
facts
)
kb
.
querySingle
{
x
=>
Seq
(
Placeholder
(
x
),
Placeholder
(
Atom
(
1
),
x
))
}
should
equal
(
Some
(
2
))
kb
.
queryFirst
{
x
=>
Seq
(
Placeholder
(
x
),
Placeholder
(
Atom
(
1
),
x
))
}
should
equal
(
Some
(
2
))
kb
.
queryFirst
{
x
=>
Seq
(
Placeholder
(
x
))
}
should
equal
(
Some
(
2
))
kb
.
queryFirst
{
x
=>
Seq
(
Placeholder
(
x
,
Atom
(
2
)))
}
should
equal
(
Some
(
1
))
kb
.
queryFirst
{
x
=>
Seq
(
Placeholder
(
Atom
(
1
),
x
))
}
should
equal
(
Some
(
2
))
}
it
should
"unify variable with unary fact
"
in
{
val
kb
=
new
KnowledgeBase
(
Seq
(
MonoFact
(
1
))
)
kb
.
query
Single
{
x
=>
Seq
(
Placeholder
(
x
))
}
should
equal
(
Some
(
1
)
)
"First-match variable binding on empty knowledge base"
should
"not be returned
"
in
{
val
kb
=
new
KnowledgeBase
(
Seq
.
empty
[
MonoFact
[
Int
]]
)
kb
.
query
First
{
x
=>
Seq
(
Placeholder
(
x
))
}
should
equal
(
None
)
}
it
should
"unify one variable in binary fact"
in
{
val
kb
=
new
KnowledgeBase
(
Seq
(
MonoFact
(
1
,
2
)))
kb
.
querySingle
{
x
=>
Seq
(
Placeholder
(
x
,
Atom
(
2
)))
}
should
equal
(
Some
(
1
))
kb
.
querySingle
{
x
=>
Seq
(
Placeholder
(
Atom
(
1
),
x
))
}
should
equal
(
Some
(
2
))
"Non-deterministic query"
should
"return valid variable binding"
ignore
{
val
kb
=
new
KnowledgeBase
(
Seq
(
MonoFact
(
1
,
1
),
MonoFact
(
3
,
3
)
))
kb
.
queryNondeterministic
{
x
:
Variable
[
Int
]
=>
Seq
(
Placeholder
(
x
,
x
))
}
should
contain
allOf
(
1
,
3
)
}
it
should
"not return value for empty knowledge base"
in
{
val
kb
=
new
KnowledgeBase
(
Seq
.
empty
[
MonoFact
[
Int
]])
kb
.
querySingle
{
x
=>
Seq
(
Placeholder
(
x
))
}
should
equal
(
None
)
"Facts with specified arity"
should
"be returned"
in
{
val
knowledgeBase
:
KnowledgeBase
[
Int
]
=
new
KnowledgeBase
(
Seq
(
MonoFact
(
0
),
MonoFact
(
1
,
20
),
MonoFact
(
1
),
MonoFact
(
20
,
1
),
MonoFact
(
0
,
0
),
MonoFact
(
1
,
0
)
))
all
(
knowledgeBase
.
withArity
(
1
).
facts
.
map
(
_
.
arity
))
shouldEqual
1
all
(
knowledgeBase
.
withArity
(
2
).
facts
.
map
(
_
.
arity
))
shouldEqual
2
}
"Single multi-variable deterministic query"
should
"return valid variable bindings"
in
{
val
x
:
Variable
[
Int
]
=
Variable
()
val
y
:
Variable
[
Int
]
=
Variable
()
val
kb
:
KnowledgeBase
[
Int
]
=
new
KnowledgeBase
(
Seq
(
MonoFact
(
2
),
MonoFact
(
1
,
1
),
MonoFact
(
2
,
2
),
MonoFact
(
1
),
MonoFact
(
2
,
1
),
MonoFact
(
1
,
2
)
))
kb
.
queryDeterministic
(
Placeholder
(
x
,
x
))
should
equal
(
List
(
Set
(
Variable
(
1
)),
Set
(
Variable
(
2
))
))
kb
.
queryDeterministic
(
Placeholder
(
x
,
y
))
should
equal
(
List
(
Set
(
Variable
(
1
),
Variable
(
1
)),
Set
(
Variable
(
2
),
Variable
(
2
)),
Set
(
Variable
(
2
),
Variable
(
1
)),
Set
(
Variable
(
1
),
Variable
(
2
))
))
kb
.
queryDeterministic
(
Placeholder
(
x
))
should
equal
(
List
(
Set
(
Variable
(
2
)),
Set
(
Variable
(
1
))
))
}
"Compound, multi-variable deterministic query"
should
"return valid variable bindings"
in
{
val
x
=
Variable
[
Int
](
name
=
Some
(
"x"
))
val
y
=
Variable
[
Int
](
name
=
Some
(
"y"
))
val
z
=
Variable
[
Int
](
name
=
Some
(
"z"
))
val
kb
=
new
KnowledgeBase
[
Int
](
Seq
(
MonoFact
(
1
,
2
),
MonoFact
(
1
,
3
),
MonoFact
(
2
,
1
),
MonoFact
(
1
)
))
kb
.
queryDeterministic
(
Seq
(
Placeholder
(
x
),
Placeholder
(
y
,
z
)))
should
equal
(
Seq
(
Set
(
Variable
(
"x"
,
1
),
Variable
(
"y"
,
1
),
Variable
(
"z"
,
2
)),
Set
(
Variable
(
"x"
,
1
),
Variable
(
"y"
,
1
),
Variable
(
"z"
,
3
)),
Set
(
Variable
(
"x"
,
1
),
Variable
(
"y"
,
2
),
Variable
(
"z"
,
1
))
))
}
}
src/test/scala/pl/edu/agh/kis/eis/MonoFactSpec.scala
0 → 100644
View file @
e4920ba8
package
pl.edu.agh.kis.eis
import
org.scalatest.
{
FlatSpec
,
Matchers
}
class
MonoFactSpec
extends
FlatSpec
with
Matchers
{
"Constant variable match"
should
"success"
in
{
val
fact
=
MonoFact
(
1
,
2
)
fact
matchesConstants
Map
(
0
->
1
)
should
be
(
true
)
fact
matchesConstants
Map
(
1
->
2
)
should
be
(
true
)
fact
matchesConstants
Map
(
0
->
1
,
1
->
2
)
should
be
(
true
)
}
"Constant variable match"
should
"fail"
in
{
val
fact
=
MonoFact
(
5
,
7
)
fact
matchesConstants
Map
(
2
->
10
)
should
be
(
false
)
// index off-by-one
// reversed values
fact
matchesConstants
Map
(
0
->
7
)
should
be
(
false
)
fact
matchesConstants
Map
(
1
->
5
)
should
be
(
false
)
}
}
src/test/scala/pl/edu/agh/kis/eis/PlaceholderSpec.scala
View file @
e4920ba8
...
...
@@ -6,8 +6,8 @@ class PlaceholderSpec extends FlatSpec with Matchers {
def
genVariables
[
A
](
n
:
Int
)
:
Seq
[
VarOrAtom
[
A
]]
=
Seq
.
tabulate
(
n
)(
_
=>
Variable
())
"Placeholder"
should
"bind single variable"
in
{
val
v
ariable
s
=
Seq
(
Variable
[
Int
]())
val
placeholder
=
Placeholder
(
variables
.
head
,
variables
.
head
)
val
x
=
V
ariable
[
Int
](
"x"
)
//
Seq(Variable[Int]())
val
placeholder
=
Placeholder
(
x
,
x
)
placeholder
.
unify
(
MonoFact
(
1
,
1
))
should
contain
only
1
}
...
...
@@ -33,4 +33,33 @@ class PlaceholderSpec extends FlatSpec with Matchers {
placeholder
.
unify
(
MonoFact
(
1
,
20
,
20
,
2
,
18
))
should
contain
inOrderOnly
(
20
,
18
)
placeholder
.
unify
(
MonoFact
(
1
,
20
,
30
,
2
,
18
))
should
be
(
empty
)
}
"Placeholder constants"
should
"be returned"
in
{
Placeholder
().
constants
should
be
(
empty
)
Placeholder
(
Variable
(),
Atom
(
1
),
Atom
(
2
),
Variable
(),
Atom
(
20
)).
constants
should
contain
inOrderOnly
(
(
1
,
1
),
(
2
,
2
),
(
4
,
20
))
Placeholder
(
Atom
(
18
),
Variable
(),
Variable
(),
Variable
(),
Atom
(
1
)).
constants
should
contain
inOrderOnly
(
(
0
,
18
),
(
4
,
1
)
)
}
"Placeholder variables"
should
"be returned"
in
{
Placeholder
(
Atom
(
1
),
Atom
(
1
),
Variable
(
"x"
),
Variable
(
"y"
),
Atom
(
2
)).
variables
should
contain
only
(
Variable
(
"x"
),
Variable
(
"y"
)
)
}
"Provided variables"
should
"be compatible with Placeholder"
in
{
Placeholder
(
Variable
(
"x"
,
5
)).
isBindingCompatible
(
Set
(
Variable
(
"x"
,
5
)))
should
equal
(
true
)
}
"Provided variables"
should
"not be compatible with Placeholder"
in
{
Placeholder
(
Variable
(
"x"
,
5
)).
isBindingCompatible
(
Set
(
Variable
(
"x"
,
10
)))
should
equal
(
false
)
}
"Unrelated variable"
should
"be compatible with any Placeholder"
in
{
Placeholder
(
Variable
(
"y"
,
20
)).
isBindingCompatible
(
Set
(
Variable
(
"x"
,
20
)))
should
equal
(
true
)
Placeholder
(
Atom
(
1
),
Atom
(
2
)).
isBindingCompatible
(
Set
(
Variable
(
"x"
,
40
)))
should
equal
(
true
)
}
}
src/test/scala/pl/edu/agh/kis/eis/VarOrAtomSpec.scala
0 → 100644
View file @
e4920ba8
package
pl.edu.agh.kis.eis
import
org.scalatest.
{
FlatSpec
,
Matchers
}
class
VarOrAtomSpec
extends
FlatSpec
with
Matchers
{
val
knownBindings
:
Seq
[
Set
[
Variable
[
Int
]]]
=
Seq
(
Set
(
Variable
(
"x"
,
1
),
Variable
(
"y"
,
2
)),
Set
(
Variable
(
"x"
,
2
),
Variable
(
"y"
,
1
))
)
"Adding compatible binding"
should
"return known input bindings back"
in
{
Variable
.
augmentBindings
(
knownBindings
,
Set
(
Variable
[
Int
](
"x"
,
1
)))
should
equal
(
Seq
(
knownBindings
.
head
))
Variable
.
augmentBindings
(
knownBindings
,
Set
(
Variable
[
Int
](
"y"
,
1
)))
should
equal
(
Seq
(
knownBindings
(
1
)))
}
"Adding non-existent variable"
should
"augment existing bindings with new element"
in
{
Variable
.
augmentBindings
(
knownBindings
,
Set
(
Variable
[
Int
](
"z"
,
3
)))
should
equal
(
Seq
[
Set
[
Variable
[
Int
]]](
Set
(
Variable
(
"x"
,
1
),
Variable
(
"y"
,
2
),
Variable
[
Int
](
"z"
,
3
)),
Set
(
Variable
(
"x"
,
2
),
Variable
(
"y"
,
1
),
Variable
[
Int
](
"z"
,
3
))
))
}
"Adding conflicting binding should"
should
"discard whole binding"
in
{
Variable
.
augmentBindings
(
knownBindings
,
Set
(
Variable
[
Int
](
"x"
,
10
)))
should
be
(
empty
)
Variable
.
augmentBindings
(
knownBindings
,
Set
(
Variable
[
Int
](
"y"
,
20
)))
should
be
(
empty
)
}
"Bindings"
should
"be augmented when there are no known bindings"
in
{
val
candidateBindings
:
Set
[
Variable
[
Int
]]
=
Set
(
Variable
(
"x"
,
1
),
Variable
(
"y"
,
2
))
Variable
.
augmentBindings
(
Seq
.
empty
,
candidateBindings
)
should
equal
(
Seq
(
candidateBindings
))
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment