Commit 075cc6cf authored by Brice Videau's avatar Brice Videau

Added parser to python bindings.

parent f9388fcb
......@@ -12,6 +12,7 @@ from .interval import *
from .distribution import *
from .hyperparameter import *
from .expression import *
from .expression_parser import *
from .context import *
from .configuration_space import *
from .configuration import *
......
......@@ -3,7 +3,9 @@ from .base import Object, Error, ccs_error, _ccs_get_function, ccs_context, ccs_
from .context import Context
from .hyperparameter import Hyperparameter
from .expression import Expression
from .expression_parser import ccs_parser
from .rng import Rng
from parglare.parser import Context as PContext
ccs_create_configuration_space = _ccs_get_function("ccs_create_configuration_space", [ct.c_char_p, ct.c_void_p, ct.POINTER(ccs_configuration_space)])
ccs_configuration_space_set_rng = _ccs_get_function("ccs_configuration_space_set_rng", [ccs_configuration_space, ccs_rng])
......@@ -70,6 +72,8 @@ class ConfigurationSpace(Context):
Error.check(res)
def set_condition(self, hyperparameter, expression):
if isinstance(expression, str):
expression = ccs_parser.parse(expression, context = PContext(extra=self))
if isinstance(hyperparameter, Hyperparameter):
hyperparameter = self.hyperparameter_index(hyperparameter)
elif isinstance(hyperparameter, str):
......@@ -105,6 +109,8 @@ class ConfigurationSpace(Context):
return [Expression.from_handle(ccs_expression(x)) if x else None for x in v]
def add_forbidden_clause(self, expression):
if isinstance(expression, str):
expression = ccs_parser.parse(expression, context = PContext(extra=self))
res = ccs_configuration_space_add_forbidden_clause(self.handle, expression.handle)
Error.check(res)
......@@ -112,6 +118,7 @@ class ConfigurationSpace(Context):
sz = len(expressions)
if sz == 0:
return None
expressions = [ ccs_parser.parse(expression, context = PContext(extra=self)) if isinstance(expression, str) else expression for expression in expressions ]
v = (ccs_expression * sz)(*[x.handle.value for x in expressions])
res = ccs_configuration_space_add_forbidden_clauses(self.handle, sz, v)
Error.check(res)
......
from parglare import Parser, Grammar
from .expression import Expression, Literal, Variable, List, ccs_expression_type, ccs_associativity_type, ccs_expression_precedence, ccs_expression_associativity, ccs_expression_symbols, ccs_expression_arity
_associativity_map = {
ccs_associativity_type.LEFT_TO_RIGHT: "left",
ccs_associativity_type.RIGHT_TO_LEFT: "right"
}
ccs_grammar = "expr: '(' expr ')'\n"
for i in range(ccs_expression_type.OR, ccs_expression_type.IN):
if ccs_expression_arity[i] == 1:
ccs_grammar += " | '{}' expr {{{}, {}}}\n".format(ccs_expression_symbols[i], _associativity_map[ccs_expression_associativity[i].value], ccs_expression_precedence[i])
else:
ccs_grammar += " | expr '{}' expr {{{}, {}}}\n".format(ccs_expression_symbols[i], _associativity_map[ccs_expression_associativity[i].value], ccs_expression_precedence[i])
ccs_grammar += " | expr '{}' list {{{}, {}}}".format(ccs_expression_symbols[ccs_expression_type.IN], _associativity_map[ccs_expression_associativity[ccs_expression_type.IN].value], ccs_expression_precedence[ccs_expression_type.IN])
ccs_grammar += r"""
| value;
list: '[' list_item ']'
| '[' ']';
list_item: list_item ',' value
| value;
value: none
| boolean
| string
| identifier
| integer
| float;
terminals
none: /None/ {prefer};
boolean: /True|False/ {prefer};
identifier: /[a-zA-Z_][a-zA-Z_0-9]*/;
string: /"([^\0\t\n\r\f"\\]|\\[0tnrf"\\])+"|'([^\0\t\n\r\f'\\]|\\[0tnrf'\\])+'/;
integer: /-?[0-9]+/;
float: /-?[0-9]+([eE][+-]?[0-9]+|\.[0-9]+([eE][+-]?[0-9]+)?)/;
"""
_actions = {}
_expr_actions = [ lambda _, n: n[1] ]
for i in range(ccs_expression_type.OR, ccs_expression_type.LIST):
if ccs_expression_arity[i] == 1:
_expr_actions.append((lambda s: lambda _, n: Expression.unary(t = s, node = n[1]))(i))
else:
_expr_actions.append((lambda s: lambda _, n: Expression.binary(t = s, left = n[0], right = n[2]))(i))
_expr_actions.append(lambda _, n: n[0])
_actions["expr"] = _expr_actions
_actions["list"] = [
lambda _, n: List(values = n[1]),
lambda _, n: List(values = [])
]
_actions["list_item"] = [
lambda _, n: n[0] + [n[2]],
lambda _, n: [n[0]]
]
_actions["none"] = lambda _, value: Literal(value = eval(value))
_actions["boolean"] = lambda _, value: Literal(value = eval(value))
_actions["identifier"] = lambda p, value: Variable(hyperparameter = p.extra.hyperparameter_by_name(value))
_actions["string"] = lambda _, value: Literal(value = eval(value))
_actions["float"] = lambda _, value: Literal(value = float(value))
_actions["integer"] = lambda _, value: Literal(value = int(value))
_g = Grammar.from_string(ccs_grammar)
ccs_parser = Parser(_g, actions=_actions)
......@@ -3,8 +3,10 @@ from .base import Object, Error, CEnumeration, ccs_error, _ccs_get_function, ccs
from .context import Context
from .hyperparameter import Hyperparameter
from .expression import Expression
from .expression_parser import ccs_parser
from .configuration_space import ConfigurationSpace
from .configuration import Configuration
from parglare.parser import Context as PContext
class ccs_objective_type(CEnumeration):
_members_ = [
......@@ -46,6 +48,8 @@ class ObjectiveSpace(Context):
Error.check(res)
def add_objective(self, expression, t = ccs_objective_type.MINIMIZE):
if isinstance(expression, str):
expression = ccs_parser.parse(expression, context = PContext(extra=self))
res = ccs_objective_space_add_objective(self.handle, expression.handle, t)
Error.check(res)
......@@ -53,6 +57,7 @@ class ObjectiveSpace(Context):
if isinstance(expressions, dict):
types = expressions.values()
expressions = expressions.keys()
expressions = [ ccs_parser.parse(expression, context = PContext(extra=self)) if isinstance(expression, str) else expression for expression in expressions ]
sz = len(expressions)
if sz == 0:
return None
......
......@@ -156,8 +156,83 @@ class TestConfigurationSpace(unittest.TestCase):
self.assertNotEqual( ccs.ccs_inactive, s.value(par) )
for par in list(set(all_params) - set(active_params)):
self.assertEqual( ccs.ccs_inactive, s.value(par) )
self.assertFalse( s.value('p1') == '#pragma omp #P2' and s.value('p2') == ' ' )
self.assertFalse( s.value('p1') == '#pragma omp #P3' and s.value('p3') == ' ' )
self.assertFalse( s.value('p1') == '#pragma omp #P2' and s.value('p2') == ' ' )
self.assertFalse( s.value('p1') == '#pragma omp #P3' and s.value('p3') == ' ' )
def test_omp_parse(self):
p1 = ccs.CategoricalHyperparameter(
name = 'p1',
values = [
' ',
'#pragma omp #P2',
'#pragma omp target teams distribute #P2',
'#pragma omp target teams distribute #P4',
'#pragma omp #P3'])
p2 = ccs.CategoricalHyperparameter(
name = 'p2',
values = [
' ',
'parallel for #P3',
'parallel for #P5',
'parallel for #P6'])
p3 = ccs.CategoricalHyperparameter(
name = 'p3',
values = [' ', 'simd'])
p4 = ccs.CategoricalHyperparameter(
name = 'p4',
values = [
' ',
'dist_schedule(static)',
'dist_schedule(static, #P8)'])
p5 = ccs.CategoricalHyperparameter(
name = 'p5',
values = [
' ',
'schedule(#P7,#P8)',
'schedule(#P7)'])
p6 = ccs.CategoricalHyperparameter(
name = 'p6',
values = [
' ',
'numthreads(#P9)'])
p7 = ccs.CategoricalHyperparameter(
name = 'p7',
values = [
'static',
'dynamic'])
p8 = ccs.OrdinalHyperparameter(
name = 'p8',
values = ['1', '8', '16'])
p9 = ccs.OrdinalHyperparameter(
name = 'p9',
values = ['1', '8', '16'])
cs = ccs.ConfigurationSpace(name = "omp")
cs.add_hyperparameters([p1, p2, p3, p4, p5, p6, p7, p8, p9])
cs.set_condition(p2, "p1 # ['#pragma omp #P2', '#pragma omp target teams distribute #P2']")
cs.set_condition(p4, "p1 == '#pragma omp target teams distribute #P4'")
cs.set_condition(p3, "p1 == '#pragma omp #P3' || p2 == 'parallel for #P3'")
cs.set_condition(p5, "p2 == 'parallel for #P5'")
cs.set_condition(p6, "p2 == 'parallel for #P6'")
cs.set_condition(p7, "p5 # ['schedule(#P7)', 'schedule(#P7,#P8)']")
cs.set_condition(p8, "p4 == 'dist_schedule(static, #P8)' || p5 == 'schedule(#P7,#P8)'")
cs.set_condition(p9, "p6 == 'numthreads(#P9)'")
cs.add_forbidden_clauses(["p1 == '#pragma omp #P2' && p2 == ' '",
"p1 == '#pragma omp #P3' && p3 == ' '"])
all_params = [ "p{}".format(i) for i in range(1,10) ]
for i in range(1000):
s = cs.sample()
s.check()
active_params = self.extract_active_parameters(s.values)
for par in active_params:
self.assertNotEqual( ccs.ccs_inactive, s.value(par) )
for par in list(set(all_params) - set(active_params)):
self.assertEqual( ccs.ccs_inactive, s.value(par) )
self.assertFalse( s.value('p1') == '#pragma omp #P2' and s.value('p2') == ' ' )
self.assertFalse( s.value('p1') == '#pragma omp #P3' and s.value('p3') == ' ' )
if __name__ == '__main__':
......
import unittest
import sys
sys.path.insert(1, '.')
sys.path.insert(1, '..')
import cconfigspace as ccs
class TestExpressionParser(unittest.TestCase):
def test_parse(self):
exp = "1.0 + 1 == 2 || +1 == 3e0 && \"y\\nes\" == 'no' "
res = ccs.ccs_parser.parse(exp)
self.assertIsInstance(res, ccs.Expression)
self.assertEqual( "1.0 + 1 == 2 || +1 == 3.0 && 'y\\nes' == 'no'", res.__str__() )
def test_parse_priority(self):
exp = "(1 + 3) * 2"
res = ccs.ccs_parser.parse(exp)
self.assertIsInstance(res, ccs.Expression)
self.assertEqual( exp, res.__str__() )
def test_associativity(self):
exp = "5 - 2 - 1"
res = ccs.ccs_parser.parse(exp)
self.assertIsInstance( res, ccs.Expression )
self.assertEqual( 2, res.eval() )
exp = "5 - +(+2 - 1)"
res = ccs.ccs_parser.parse(exp)
self.assertIsInstance( res, ccs.Expression )
self.assertEqual( 4, res.eval() )
def test_in(self):
exp = "5 # [3.0, 5]"
res = ccs.ccs_parser.parse(exp)
self.assertIsInstance( res, ccs.Expression )
self.assertTrue( res.eval() )
exp = "5 # [3.0, 4]"
res = ccs.ccs_parser.parse(exp)
self.assertIsInstance( res, ccs.Expression )
self.assertFalse( res.eval() )
def test_boolean(self):
exp = "True"
res = ccs.ccs_parser.parse(exp)
self.assertIsInstance( res, ccs.Literal )
self.assertEqual( True, res.eval() )
self.assertEqual( "True", res.__str__() )
exp = "False"
res = ccs.ccs_parser.parse(exp)
self.assertIsInstance( res, ccs.Literal )
self.assertEqual( False, res.eval() )
self.assertEqual( "False", res.__str__() )
def test_none(self):
exp = "None"
res = ccs.ccs_parser.parse(exp)
self.assertIsInstance( res, ccs.Literal )
self.assertIsNone( res.eval() )
self.assertEqual( "None", res.__str__() )
if __name__ == '__main__':
unittest.main()
......@@ -206,7 +206,7 @@ module CCS
def to_s
case value
when String
when nil, String
value.inspect
else
value.to_s
......
......@@ -43,6 +43,10 @@ module CCS
rule("]")
rule(",")
rule(:none => /nil/).as { |b|
Literal::new(value: nil) }
rule(:boolean => /true|false/).as { |b|
Literal::new(value: eval(b)) }
rule(:float => /-?[0-9]+([eE][+-]?[0-9]+|\.[0-9]+([eE][+-]?[0-9]+)?)/).as {|num|
Literal::new(value: Float(num)) }
rule(:integer => /-?[0-9]+/).as { |num|
......@@ -53,6 +57,8 @@ module CCS
Literal::new(value: eval(str)) }
rule(:value) do |r|
r[:none]
r[:boolean]
r[:string]
r[:identifier]
r[:float]
......
......@@ -46,5 +46,29 @@ class CConfigSpaceTestExpressionParser < Minitest::Test
assert( res.kind_of? CCS::Expression )
assert_equal( false, res.eval )
end
def test_boolean
m = CCS::ExpressionParser.new.method(:parse)
exp = "true"
res = m[exp]
assert( res.kind_of? CCS::Literal )
assert_equal( true, res.eval )
assert_equal( "true", res.to_s )
exp = "false"
res = m[exp]
assert( res.kind_of? CCS::Literal )
assert_equal( false, res.eval )
assert_equal( "false", res.to_s )
end
def test_none
m = CCS::ExpressionParser.new.method(:parse)
exp = "nil"
res = m[exp]
assert( res.kind_of? CCS::Literal )
assert_nil( res.eval )
assert_equal( "nil", res.to_s )
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment