Add and use Fact and FactSet

Fact is a class used to store a single Fact.  It replaces Rule in cases where
the Fact has no negation and all the variables are bound.  The Fact uses much
less memory than a Rule for these situations since the Fact is really just a
native tuple.

FactSet is a container for Facts.  RuleSet will now use a FactSet inside to
store what we used to call literals.

The RuleSet now converts Facts to Rules before returning the rules for
get_rules(), so this change makes MaterializedView use OrderedSet directly
since MaterializedView uses DeltaRules instead of Rules.

Implements blueprint: fact-datastructure
Change-Id: I5ac47075288f50fe027b94abc76724cb18370f3c
This commit is contained in:
Alexander Yip
2015-01-22 16:43:59 -08:00
parent ff935ff8be
commit ef43aea498
12 changed files with 482 additions and 133 deletions

View File

@@ -142,6 +142,16 @@ class Theory(object):
else:
self.trace_prefix = self.abbr + " " * (maxlength - len(self.abbr))
def initialize_tables(self, tablenames, facts):
"""initialize_tables
Event handler for (re)initializing a collection of tables. Clears
tables befores assigning the new table content.
@facts must be an iterable containing compile.Fact objects.
"""
raise NotImplementedError
def actual_events(self, events):
"""Returns subset of EVENTS that are not noops."""
actual = []

View File

@@ -182,6 +182,23 @@ class ObjectConstant (Term):
return True
class Fact (tuple):
"""Represent a Fact (a ground literal)
Use this class to represent a fact such as Foo(1,2,3). While one could
use a Rule to represent the same fact, this Fact datastructure is more
memory efficient than a Rule object since this Fact stores the information
as a native tuple, containing native values like ints and strings. Notes
that this subclasses from tuple.
"""
def __new__(cls, table, values):
return super(Fact, cls).__new__(cls, values)
def __init__(self, table, values):
super(Fact, self).__init__(table, values)
self.table = table
class Literal (object):
"""Represents a possibly negated atomic statement, e.g. p(a, 17, b)."""

View File

@@ -76,23 +76,14 @@ class DseRuntime (runtime.Runtime, deepsix.deepSix):
"""Handler for when dataservice publishes full table."""
self.log("received full data msg for %s: %s",
msg.header['dataindex'], runtime.iterstr(msg.body.data))
literals = []
tablename = msg.header['dataindex']
service = msg.replyTo
for row in msg.body.data:
if not isinstance(row, tuple):
raise ValueError("Tuple expected, received: %s" % row)
# prefix tablename with data source
literals.append(compile.Literal.create_from_table_tuple(
tablename, row))
(permitted, changes) = self.initialize_tables(
[tablename], literals, target=service)
if not permitted:
raise runtime.CongressRuntime(
"Update not permitted." + '\n'.join(str(x) for x in changes))
else:
self.log("full data msg for %s caused %d changes: %s",
tablename, len(changes), runtime.iterstr(changes))
# Use a generator to avoid instantiating all these Facts at once.
literals = (compile.Fact(tablename, row) for row in msg.body.data)
self.initialize_tables([tablename], literals, target=service)
self.log("full data msg for %s", tablename)
def receive_data_update(self, msg):
"""Handler for when dataservice publishes a delta."""

148
congress/policy/factset.py Normal file
View File

@@ -0,0 +1,148 @@
# Copyright (c) 2015 VMware, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
from congress.openstack.common import log as logging
from congress.policy import utility
LOG = logging.getLogger(__name__)
class FactSet(object):
"""FactSet
Maintains a set of facts, and provides indexing for efficient iteration,
given a partial or full match. Expects that all facts are the same width.
"""
def __init__(self):
self._facts = utility.OrderedSet()
# key is a sorted tuple of column indices, values are dict mapping a
# specific value for the key to a set of Facts.
self._indicies = {}
def __contains__(self, fact):
return fact in self._facts
def __len__(self):
return len(self._facts)
def __iter__(self):
return self._facts.__iter__()
def add(self, fact):
"""Add a fact to the FactSet
Returns True if the fact is absent from this FactSet and adds the
fact, otherwise returns False.
"""
assert isinstance(fact, tuple)
changed = self._facts.add(fact)
if changed:
# Add the fact to the indicies
for index in self._indicies.keys():
self._add_fact_to_index(fact, index)
return changed
def remove(self, fact):
"""Remove a fact from the FactSet
Returns True if the fact is in this FactSet and removes the fact,
otherwise returns False.
"""
changed = self._facts.discard(fact)
if changed:
# Remove from indices
for index in self._indicies.keys():
self._remove_fact_from_index(fact, index)
return changed
def create_index(self, columns):
"""Create an index
@columns is a tuple of column indicies that index into the facts in
self. @columns must be sorted in ascending order, and each column
index must be less than the width of a fact in self. If the index
exists, do nothing.
"""
assert sorted(columns) == list(columns)
assert len(columns)
if columns in self._indicies:
return
for f in self._facts:
self._add_fact_to_index(f, columns)
def remove_index(self, columns):
"""Remove an index
@columns is a tuple of column indicies that index into the facts in
self. @columns must be sorted in ascending order, and each column
index must be less than the width of a fact in self. If the index
does not exists, raise KeyError.
"""
assert sorted(columns) == list(columns)
if columns in self._indicies:
del self._indicies[columns]
def has_index(self, columns):
"""Returns True if the index exists."""
return columns in self._indicies
def find(self, partial_fact):
"""Find Facts given a partial fact
@partial_fact is a tuple of pair tuples. The first item in each
pair tuple is an index into a fact, and the second item is a value to
match again self._facts. Expects the pairs to be sorted by index in
ascending order.
"""
index = tuple([i for i, v in partial_fact])
k = tuple([v for i, v in partial_fact])
if index in self._indicies and k in self._indicies[index]:
return self._indicies[index][k]
# There is no index, so iterate.
matches = set()
for f in self._facts:
match = True
for i, v in partial_fact:
if f[i] != v:
match = False
break
if match:
matches.add(f)
return matches
def _compute_key(self, columns, fact):
# assumes that @columns is sorted in ascending order.
return tuple([fact[i] for i in columns])
def _add_fact_to_index(self, fact, index):
if index not in self._indicies:
self._indicies[index] = {}
k = self._compute_key(index, fact)
if k not in self._indicies[index]:
self._indicies[index][k] = set((fact,))
else:
self._indicies[index][k].add(fact)
def _remove_fact_from_index(self, fact, index):
k = self._compute_key(index, fact)
self._indicies[index][k].remove(fact)
if not len(self._indicies[index][k]):
del self._indicies[index][k]

View File

@@ -23,9 +23,9 @@ from congress.policy.builtin.congressbuiltin import builtin_registry
from congress.policy import compile
from congress.policy.compile import Event
from congress.policy.database import Database
from congress.policy.ruleset import RuleSet
from congress.policy.topdown import TopDownTheory
from congress.policy.utility import iterstr
from congress.policy.utility import OrderedSet
LOG = logging.getLogger(__name__)
@@ -75,7 +75,7 @@ class DeltaRuleTheory (Theory):
name=name, abbr=abbr, theories=theories)
# dictionary from table name to list of rules with that table as
# trigger
self.rules = RuleSet()
self.rules = {}
# dictionary from delta_rule to the rule from which it was derived
self.originals = set()
# dictionary from table name to number of rules with that table in
@@ -137,7 +137,9 @@ class DeltaRuleTheory (Theory):
# contents
# TODO(thinrichs): eliminate dups, maybe including
# case where bodies are reorderings of each other
self.rules.add_rule(delta.trigger.table, delta)
if delta.trigger.table not in self.rules:
self.rules[delta.trigger.table] = OrderedSet()
self.rules[delta.trigger.table].add(delta)
def delete(self, rule):
"""Delete a compile.Rule from theory.
@@ -169,7 +171,9 @@ class DeltaRuleTheory (Theory):
del self.all_tables[table]
# contents
self.rules.discard_rule(delta.trigger.table, delta)
self.rules[delta.trigger.table].discard(delta)
if not len(self.rules[delta.trigger.table]):
del self.rules[delta.trigger.table]
def policy(self):
return self.originals
@@ -189,7 +193,7 @@ class DeltaRuleTheory (Theory):
def rules_with_trigger(self, table):
"""Return the list of DeltaRules that trigger on the given TABLE."""
if table in self.rules:
return self.rules.get_rules(table)
return self.rules[table]
else:
return []

View File

@@ -40,6 +40,27 @@ class NonrecursiveRuleTheory(TopDownTheory):
# SELECT implemented by TopDownTheory
def initialize_tables(self, tablenames, facts):
"""Event handler for (re)initializing a collection of tables
@facts must be an iterable containing compile.Fact objects.
"""
LOG.info("initialize_tables")
cleared_tables = set(tablenames)
for t in tablenames:
self.rules.clear_table(t)
count = 0
for f in facts:
if f.table not in cleared_tables:
self.rules.clear_table(f.table)
cleared_tables.add(f.table)
self.rules.add_rule(f.table, f)
count += 1
LOG.info("initialized %d tables with %d facts",
len(cleared_tables), count)
def insert(self, rule):
changes = self.update([Event(formula=rule, insert=True)])
return [event.formula for event in changes]

View File

@@ -14,6 +14,10 @@
#
from congress.openstack.common import log as logging
from congress.policy.compile import Fact
from congress.policy.compile import Literal
from congress.policy.compile import Rule
from congress.policy.factset import FactSet
from congress.policy import utility
LOG = logging.getLogger(__name__)
@@ -22,8 +26,7 @@ LOG = logging.getLogger(__name__)
class RuleSet(object):
"""RuleSet
Keeps track of all rules for all tables. Also manages indicies that allow
a caller to get all rules that match a certain literal pattern.
Keeps track of all rules for all tables.
"""
# Internally:
@@ -34,106 +37,120 @@ class RuleSet(object):
def __init__(self):
self.rules = {}
self.literals = {}
self.indicies = {}
self.facts = {}
def __str__(self):
return str(self.rules) + " " + str(self.literals)
return str(self.rules) + " " + str(self.facts)
def add_rule(self, key, rule):
# rule can be a rule or a literal
# returns True on change
"""Add a rule to the Ruleset
if len(rule.body):
dest = self.rules
else:
dest = self.literals
# Update indicies
for index_name in self.indicies.keys():
if key == index_name[0]:
self._add_literal_to_index(rule, index_name)
@rule can be a Rule or a Fact. Returns True if add_rule() changes the
RuleSet.
"""
if isinstance(rule, Fact):
# If the rule is a Fact, then add it to self.facts.
if key not in self.facts:
self.facts[key] = FactSet()
return self.facts[key].add(rule)
elif len(rule.body) == 0 and not rule.head.is_negated():
# If the rule is a Rule, with no body, then it's a Fact, so
# convert the Rule to a Fact to a Fact and add to self.facts.
f = Fact(key, (a.name for a in rule.head.arguments))
if key not in self.facts:
self.facts[key] = FactSet()
return self.facts[key].add(f)
if key in dest:
return dest[key].add(rule)
else:
dest[key] = utility.OrderedSet([rule])
return True
# else the rule is a regular rule, so add it to self.rules.
if key in self.rules:
return self.rules[key].add(rule)
else:
self.rules[key] = utility.OrderedSet([rule])
return True
def discard_rule(self, key, rule):
# rule can be a rule or a literal
# returns True on change
"""Remove a rule from the Ruleset
@rule can be a Rule or a Fact. Returns True if discard_rule() changes
the RuleSet.
"""
if isinstance(rule, Fact):
# rule is a Fact, so remove from self.facts
if key in self.facts:
changed = self.facts[key].remove(rule)
if len(self.facts[key]) == 0:
del self.facts[key]
return changed
return False
elif not len(rule.body):
# rule is a Rule, but without a body so it will be in self.facts.
if key in self.facts:
fact = Fact(key, [a.name for a in rule.head.arguments])
changed = self.facts[key].remove(fact)
if len(self.facts[key]) == 0:
del self.facts[key]
return changed
return False
if len(rule.body):
dest = self.rules
else:
dest = self.literals
# Update indicies
for index_name in self.indicies.keys():
if key == index_name[0]:
self._remove_literal_from_index(rule, index_name)
if key in dest:
changed = dest[key].discard(rule)
if len(dest[key]) == 0:
del dest[key]
return changed
return False
# rule is a Rule with a body, so remove from self.rules.
if key in self.rules:
changed = self.rules[key].discard(rule)
if len(self.rules[key]) == 0:
del self.rules[key]
return changed
return False
def keys(self):
return self.literals.keys() + self.rules.keys()
return self.facts.keys() + self.rules.keys()
def __contains__(self, key):
return key in self.literals or key in self.rules
return key in self.facts or key in self.rules
def get_rules(self, key, match_literal=None):
literals = []
facts = []
if match_literal and not match_literal.is_negated():
if (match_literal and not match_literal.is_negated() and
key in self.facts):
# If the caller supplies a literal to match against, then use an
# index to find the matching rules.
bound_arguments = tuple([i for i, arg
in enumerate(match_literal.arguments)
if not arg.is_variable()])
index_name = (key,) + bound_arguments
if (bound_arguments and
not self.facts[key].has_index(bound_arguments)):
# The index does not exist, so create it.
self.facts[key].create_index(bound_arguments)
index_key = tuple([(i, arg.name) for i, arg
in enumerate(match_literal.arguments)
if not arg.is_variable()])
index_key = (key,) + index_key
if index_name not in self.indicies:
self._create_index(index_name)
literals = list(self.indicies[index_name].get(index_key, ()))
partial_fact = tuple(
[(i, arg.name)
for i, arg in enumerate(match_literal.arguments)
if not arg.is_variable()])
facts = list(self.facts[key].find(partial_fact))
else:
literals = list(self.literals.get(key, ()))
# There is no usable match_literal, so get all facts for the
# table.
facts = list(self.facts.get(key, ()))
return literals + list(self.rules.get(key, ()))
# Convert native tuples to Rule objects.
# TODO(alex): This is inefficient because it creates Literal and Rule
# objects. It would be more efficient to change the TopDownTheory and
# unifier to handle Facts natively.
fact_rules = []
for fact in facts:
literal = Literal.create_from_table_tuple(key, fact)
fact_rules.append(Rule(literal, ()))
return fact_rules + list(self.rules.get(key, ()))
def clear(self):
self.rules = {}
self.literals = {}
self.facts = {}
def _create_index(self, index_name):
# Make an index over literals. An index is an OrderedSet of rules.
self.indicies[index_name] = {} # utility.OrderedSet()
if index_name[0] in self.literals:
for literal in self.literals[index_name[0]]:
self._add_literal_to_index(literal, index_name)
def _add_literal_to_index(self, literal, index_name):
index_key = ((index_name[0],) +
tuple([(i, literal.head.arguments[i].name)
for i in index_name[1:]]))
# Populate the index
if index_key not in self.indicies[index_name]:
self.indicies[index_name][index_key] = utility.OrderedSet()
self.indicies[index_name][index_key].add(literal)
def _remove_literal_from_index(self, literal, index_name):
index_key = ((index_name[0],) +
tuple([(i, literal.head.arguments[i].name)
for i in index_name[1:]]))
self.indicies[index_name][index_key].discard(literal)
if len(self.indicies[index_name][index_key]) == 0:
del self.indicies[index_name][index_key]
def clear_table(self, table):
self.rules[table] = utility.OrderedSet()
self.facts[table] = FactSet()

View File

@@ -298,39 +298,13 @@ class Runtime (object):
else:
return self.select_obj(query, self.get_target(target), trace)
def initialize_tables(self, tablenames, formulas, target=None):
"""Event handler for (re)initializing a collection of tables."""
# translate FORMULAS into list of formula objects
actual_formulas = []
formula_tables = set()
def initialize_tables(self, tablenames, facts, target=None):
"""Event handler for (re)initializing a collection of tables
if isinstance(formulas, basestring):
formulas = self.parse(formulas)
for formula in formulas:
if isinstance(formula, basestring):
formula = self.parse1(formula)
elif isinstance(formula, tuple):
formula = compile.Literal.create_from_iter(formula)
assert formula.is_atom()
actual_formulas.append(formula)
formula_tables.add(formula.table)
tablenames = set(tablenames) | formula_tables
self.table_log(None, "Initializing tables %s with %s",
iterstr(tablenames), iterstr(actual_formulas))
# implement initialization by computing the requisite
# update.
theory = self.get_target(target)
old = set(theory.content(tablenames=tablenames))
new = set(actual_formulas)
to_add = new - old
to_rem = old - new
to_add = [Event(formula_, insert=True) for formula_ in to_add]
to_rem = [Event(formula_, insert=False) for formula_ in to_rem]
self.table_log(None, "Initialize converted to update with %s and %s",
iterstr(to_add), iterstr(to_rem))
return self.update(to_add + to_rem, target=target)
@facts must be an iterable containing compile.Fact objects.
"""
target_theory = self.get_target(target)
target_theory.initialize_tables(tablenames, facts)
def insert(self, formula, target=None):
"""Event handler for arbitrary insertion (rules and facts)."""

View File

@@ -0,0 +1,118 @@
# Copyright (c) 2015 VMware, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from congress.policy.factset import FactSet
from congress.tests import base
class TestFactSet(base.TestCase):
def setUp(self):
super(TestFactSet, self).setUp()
self.factset = FactSet()
def test_empty(self):
self.assertFalse((1, 2, 3) in self.factset)
self.assertEqual(0, len(self.factset))
def test_add_one(self):
f = (1, 2, 'a')
self.factset.add(f)
self.assertEqual(1, len(self.factset))
self.assertEqual(set([f]), self.factset.find(((0, 1), (1, 2),
(2, 'a'))))
def test_add_few(self):
f1 = (1, 200, 'a')
f2 = (2, 200, 'a')
f3 = (3, 200, 'c')
self.factset.add(f1)
self.factset.add(f2)
self.factset.add(f3)
self.assertEqual(3, len(self.factset))
self.assertEqual(set([f1, f2, f3]), self.factset.find(((1, 200),)))
self.assertEqual(set([f1, f2]), self.factset.find(((2, 'a'),)))
self.assertEqual(set([f1]), self.factset.find(((0, 1), (1, 200),
(2, 'a'),)))
self.assertEqual(set(), self.factset.find(((0, 8),)))
def test_remove(self):
f1 = (1, 200, 'a')
f2 = (2, 200, 'a')
f3 = (3, 200, 'c')
self.factset.add(f1)
self.factset.add(f2)
self.factset.add(f3)
self.assertEqual(3, len(self.factset))
self.assertTrue(self.factset.remove(f1))
self.assertEqual(2, len(self.factset))
self.assertEqual(set([f2, f3]), self.factset.find(((1, 200),)))
self.assertTrue(self.factset.remove(f3))
self.assertEqual(1, len(self.factset))
self.assertEqual(set([f2]), self.factset.find(((1, 200),)))
self.assertFalse(self.factset.remove(f3))
self.assertTrue(self.factset.remove(f2))
self.assertEqual(0, len(self.factset))
self.assertEqual(set(), self.factset.find(((1, 200),)))
def test_create_index(self):
f1 = (1, 200, 'a')
f2 = (2, 200, 'a')
f3 = (3, 200, 'c')
self.factset.add(f1)
self.factset.add(f2)
self.factset.add(f3)
self.factset.create_index((1,))
self.assertEqual(set([f1, f2, f3]), self.factset.find(((1, 200),)))
self.assertEqual(set([f1, f2]), self.factset.find(((2, 'a'),)))
self.assertEqual(set([f1, f2]), self.factset.find(((1, 200),
(2, 'a'))))
self.assertEqual(set([f1]), self.factset.find(((0, 1), (1, 200),
(2, 'a'),)))
self.assertEqual(set(), self.factset.find(((0, 8),)))
self.factset.create_index((1, 2))
self.assertEqual(set([f1, f2, f3]), self.factset.find(((1, 200),)))
self.assertEqual(set([f1, f2]), self.factset.find(((2, 'a'),)))
self.assertEqual(set([f1, f2]), self.factset.find(((1, 200),
(2, 'a'))))
self.assertEqual(set([f1]), self.factset.find(((0, 1), (1, 200),
(2, 'a'),)))
self.assertEqual(set(), self.factset.find(((0, 8),)))
def test_remove_index(self):
f1 = (1, 200, 'a')
f2 = (2, 200, 'a')
f3 = (3, 200, 'c')
self.factset.add(f1)
self.factset.add(f2)
self.factset.add(f3)
self.factset.create_index((1,))
self.factset.create_index((1, 2))
self.factset.remove_index((1,))
self.factset.remove_index((1, 2))
self.assertEqual(set([f1, f2, f3]), self.factset.find(((1, 200),)))
self.assertEqual(set([f1, f2]), self.factset.find(((2, 'a'),)))
self.assertEqual(set([f1, f2]), self.factset.find(((1, 200),
(2, 'a'))))
self.assertEqual(set([f1]), self.factset.find(((0, 1), (1, 200),
(2, 'a'),)))
self.assertEqual(set(), self.factset.find(((0, 8),)))

View File

@@ -13,6 +13,7 @@
# under the License.
from congress.policy import compile
from congress.policy.compile import Fact
from congress.policy.ruleset import RuleSet
from congress.tests import base
@@ -83,6 +84,25 @@ class TestRuleSet(base.TestCase):
self.assertEqual([rule2], self.ruleset.get_rules('p2'))
self.assertTrue('p2' in self.ruleset.keys())
def test_add_fact(self):
fact1 = Fact('p', (1, 2, 3))
equivalent_rule = compile.Rule(compile.parse1('p(1,2,3)'), ())
self.assertTrue(self.ruleset.add_rule('p', fact1))
self.assertTrue('p' in self.ruleset)
self.assertEqual([equivalent_rule], self.ruleset.get_rules('p'))
self.assertEqual(['p'], self.ruleset.keys())
def test_add_equivalent_rule(self):
# equivalent_rule could be a fact because it has no body, and is
# ground.
equivalent_rule = compile.Rule(compile.parse1('p(1,2,3)'), ())
self.assertTrue(self.ruleset.add_rule('p', equivalent_rule))
self.assertTrue('p' in self.ruleset)
self.assertEqual([equivalent_rule], self.ruleset.get_rules('p'))
self.assertEqual(['p'], self.ruleset.keys())
def test_discard_rule(self):
rule1 = compile.parse1('p(x,y) :- q(x), r(y)')
self.assertTrue(self.ruleset.add_rule('p', rule1))
@@ -128,3 +148,27 @@ class TestRuleSet(base.TestCase):
self.assertFalse('p1' in self.ruleset)
self.assertFalse('p2' in self.ruleset)
self.assertEqual([], self.ruleset.keys())
def test_discard_fact(self):
fact = Fact('p', (1, 2, 3))
equivalent_rule = compile.Rule(compile.parse1('p(1,2,3)'), ())
self.assertTrue(self.ruleset.add_rule('p', fact))
self.assertTrue('p' in self.ruleset)
self.assertEqual([equivalent_rule], self.ruleset.get_rules('p'))
self.assertTrue(self.ruleset.discard_rule('p', fact))
self.assertFalse('p' in self.ruleset)
self.assertEqual([], self.ruleset.keys())
def test_discard_equivalent_rule(self):
fact = Fact('p', (1, 2, 3))
equivalent_rule = compile.Rule(compile.parse1('p(1,2,3)'), ())
self.assertTrue(self.ruleset.add_rule('p', fact))
self.assertTrue('p' in self.ruleset)
self.assertEqual([equivalent_rule], self.ruleset.get_rules('p'))
self.assertTrue(self.ruleset.discard_rule('p', equivalent_rule))
self.assertFalse('p' in self.ruleset)
self.assertEqual([], self.ruleset.keys())

View File

@@ -20,6 +20,7 @@ from congress.policy.base import ACTION_POLICY_TYPE
from congress.policy.base import DATABASE_POLICY_TYPE
from congress.policy.base import MATERIALIZED_POLICY_TYPE
from congress.policy.base import NONRECURSIVE_POLICY_TYPE
from congress.policy.compile import Fact
from congress.policy import runtime
from congress.tests import base
from congress.tests import helper
@@ -89,7 +90,8 @@ class TestRuntime(base.TestCase):
run = runtime.Runtime()
run.create_policy('test')
run.insert('p(1) p(2)')
run.initialize_tables(['p'], ['p(3)', 'p(4)'])
facts = [Fact('p', (3,)), Fact('p', (4,))]
run.initialize_tables(['p'], facts)
e = helper.datalog_equal(run.select('p(x)'), 'p(3) p(4)')
self.assertTrue(e)

View File

@@ -14,6 +14,7 @@
#
from congress.openstack.common import log as logging
from congress.policy import base
from congress.policy.compile import Fact
from congress.policy.compile import Literal
from congress.policy import runtime
from congress.tests import base as testbase
@@ -113,8 +114,10 @@ class TestRuntimePerformance(testbase.TestCase):
pass
def test_runtime_initialize_tables(self):
MAX = 1000
formulas = [('p', 1, 2, 'foo', 'bar', i) for i in range(MAX)]
MAX = 700
longstring = 'a' * 100
facts = (Fact('p', (1, 2, 'foo', 'bar', i, longstring))
for i in range(MAX))
th = NREC_THEORY
self._runtime.initialize_tables(['p'], formulas, th)
self._runtime.initialize_tables(['p'], facts, th)