Skip to content

Commit 3cac866

Browse files
committed
Simplify the Rule API
Only attach rules to variables (and variable blocks) as strings. Also add notes to the `Rule` object docstring. TODO: Add some rule serialization examples. related_to #479
1 parent c1c4c35 commit 3cac866

5 files changed

Lines changed: 42 additions & 99 deletions

File tree

doc/samples/samples.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ Samples
9797
third_dictionary.add_variable_from_spec(
9898
name="computed",
9999
type="Numerical",
100-
rule=kh.Rule("Ceil", kh.Rule("Product", 3, kh.Rule("Random()"))),
100+
rule=str(kh.Rule("Ceil", kh.Rule("Product", 3, kh.Rule("Random()")))),
101101
)
102102
103103
# Add the variables used in a multi-table context in the first dictionary.
@@ -649,15 +649,15 @@ Samples
649649
650650
# Create fold indexing rule and set it on `fold_index_variable`
651651
fold_index_variable = dictionary.get_variable("FoldIndex")
652-
fold_index_variable.set_rule(
652+
fold_index_variable.rule = str(
653653
kh.Rule("Ceil", kh.Rule("Product", fold_number, kh.Rule("Random()"))),
654654
)
655655
656656
# Add variables that indicate if the instance is in the train dataset:
657657
for fold_index in range(1, fold_number + 1):
658658
name = "IsInTrainDataset" + str(fold_index)
659659
dictionary.add_variable_from_spec(name=name, type="Numerical", used=False)
660-
dictionary.get_variable(name).set_rule(
660+
dictionary.get_variable(name).rule = str(
661661
kh.Rule("NEQ", fold_index_variable, fold_index),
662662
)
663663

khiops/core/dictionary.py

Lines changed: 17 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -857,8 +857,8 @@ def add_variable_from_spec(
857857
Object type. Ignored if variable type not in ["Entity", "Table"].
858858
structure_type : str, optional
859859
Structure type. Ignored if variable type is not "Structure".
860-
rule : `Rule`, optional
861-
Variable rule.
860+
rule : str, optional
861+
String representation of a variable rule.
862862
meta_data : dict, optional
863863
A Python dictionary which holds the metadata specification.
864864
The dictionary keys are str. The values can be str, bool, float or int.
@@ -906,8 +906,8 @@ def add_variable_from_spec(
906906
type_error_message("structure_type", structure_type, "string-like")
907907
)
908908
if rule is not None:
909-
if not isinstance(rule, Rule):
910-
raise TypeError(type_error_message("rule", rule, Rule))
909+
if not isinstance(rule, str):
910+
raise TypeError(type_error_message("rule", rule, str))
911911

912912
# Variable initialization
913913
variable = Variable()
@@ -923,7 +923,7 @@ def add_variable_from_spec(
923923
if structure_type is not None:
924924
variable.structure_type = structure_type
925925
if rule is not None:
926-
variable.set_rule(rule)
926+
variable.rule = str(rule)
927927
self.add_variable(variable)
928928

929929
def remove_variable(self, variable_name):
@@ -1363,34 +1363,6 @@ def full_type(self):
13631363
full_type += f"({self.structure_type})"
13641364
return full_type
13651365

1366-
def get_rule(self):
1367-
"""Gets the rule of the variable
1368-
1369-
Returns
1370-
-------
1371-
`Rule`
1372-
A `Rule` instance created as a verbatim rule from the ``rule``
1373-
attribute of the variable.
1374-
"""
1375-
return Rule(verbatim=self.rule, is_reference=self.is_reference_rule())
1376-
1377-
def set_rule(self, rule):
1378-
"""Sets a rule on a specified variable in the dictionary
1379-
1380-
Parameters
1381-
----------
1382-
rule : `Rule`
1383-
The rule to be set on the variable.
1384-
1385-
Raises
1386-
------
1387-
`TypeError`
1388-
If ``rule`` is not of type `Rule`.
1389-
"""
1390-
if not isinstance(rule, Rule):
1391-
raise TypeError(type_error_message("rule", rule, Rule))
1392-
self.rule = repr(rule)
1393-
13941366
def write(self, writer):
13951367
"""Writes the domain to a file writer in ``.kdic`` format
13961368
@@ -1576,39 +1548,6 @@ def get_value(self, key):
15761548
"""
15771549
return self.meta_data.get_value(key)
15781550

1579-
def get_rule(self):
1580-
"""Gets the rule of the variable block
1581-
1582-
Returns
1583-
-------
1584-
`Rule`
1585-
A `Rule` instance created as a verbatim rule from the ``rule``
1586-
attribute of the variable block.
1587-
"""
1588-
return Rule(verbatim=self.rule)
1589-
1590-
def set_rule(self, rule):
1591-
"""Sets a rule on a specified variable block in the dictionary
1592-
1593-
Parameters
1594-
----------
1595-
rule : `Rule`
1596-
The rule to be set on the variable block.
1597-
1598-
Raises
1599-
------
1600-
`TypeError`
1601-
If ``rule`` is not of type `Rule`.
1602-
1603-
`ValueError`
1604-
If ``rule`` is a reference rule.
1605-
"""
1606-
if not isinstance(rule, Rule):
1607-
raise TypeError(type_error_message("rule", rule, Rule))
1608-
if rule.is_reference:
1609-
raise ValueError("Cannot set reference rule on a variable block")
1610-
self.rule = repr(rule)
1611-
16121551
def write(self, writer):
16131552
"""Writes the variable block to a file writer in ``.kdic`` format
16141553
@@ -1664,6 +1603,18 @@ def write(self, writer):
16641603
class Rule:
16651604
"""A rule of a variable or variable block in a Khiops dictionary
16661605
1606+
.. note::
1607+
This object is a convenience feature which easies rule creation and
1608+
serialization, especially in rather complex cases (rule operands which
1609+
are variables or rules themselves, sometimes upper-scoped). Rules
1610+
continue be attached *as strings* to variables and variable blocks.
1611+
1612+
.. note::
1613+
Rule object instances can be created either from full operand
1614+
specifications, or from verbatim rules. The latter is useful when the
1615+
rule is retrieved from an existing variable or variable block and is
1616+
used as an operand in another rule.
1617+
16671618
Parameters
16681619
----------
16691620
name_and_operands : tuple

khiops/samples/samples.ipynb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@
109109
"third_dictionary.add_variable_from_spec(\n",
110110
" name=\"computed\",\n",
111111
" type=\"Numerical\",\n",
112-
" rule=kh.Rule(\"Ceil\", kh.Rule(\"Product\", 3, kh.Rule(\"Random()\"))),\n",
112+
" rule=str(kh.Rule(\"Ceil\", kh.Rule(\"Product\", 3, kh.Rule(\"Random()\")))),\n",
113113
")\n",
114114
"\n",
115115
"# Add the variables used in a multi-table context in the first dictionary.\n",
@@ -869,15 +869,15 @@
869869
"\n",
870870
"# Create fold indexing rule and set it on `fold_index_variable`\n",
871871
"fold_index_variable = dictionary.get_variable(\"FoldIndex\")\n",
872-
"fold_index_variable.set_rule(\n",
872+
"fold_index_variable.rule = str(\n",
873873
" kh.Rule(\"Ceil\", kh.Rule(\"Product\", fold_number, kh.Rule(\"Random()\"))),\n",
874874
")\n",
875875
"\n",
876876
"# Add variables that indicate if the instance is in the train dataset:\n",
877877
"for fold_index in range(1, fold_number + 1):\n",
878878
" name = \"IsInTrainDataset\" + str(fold_index)\n",
879879
" dictionary.add_variable_from_spec(name=name, type=\"Numerical\", used=False)\n",
880-
" dictionary.get_variable(name).set_rule(\n",
880+
" dictionary.get_variable(name).rule = str(\n",
881881
" kh.Rule(\"NEQ\", fold_index_variable, fold_index),\n",
882882
" )\n",
883883
"\n",

khiops/samples/samples.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ def create_dictionary_domain():
111111
third_dictionary.add_variable_from_spec(
112112
name="computed",
113113
type="Numerical",
114-
rule=kh.Rule("Ceil", kh.Rule("Product", 3, kh.Rule("Random()"))),
114+
rule=str(kh.Rule("Ceil", kh.Rule("Product", 3, kh.Rule("Random()")))),
115115
)
116116

117117
# Add the variables used in a multi-table context in the first dictionary.
@@ -724,15 +724,15 @@ def train_predictor_with_cross_validation():
724724

725725
# Create fold indexing rule and set it on `fold_index_variable`
726726
fold_index_variable = dictionary.get_variable("FoldIndex")
727-
fold_index_variable.set_rule(
727+
fold_index_variable.rule = str(
728728
kh.Rule("Ceil", kh.Rule("Product", fold_number, kh.Rule("Random()"))),
729729
)
730730

731731
# Add variables that indicate if the instance is in the train dataset:
732732
for fold_index in range(1, fold_number + 1):
733733
name = "IsInTrainDataset" + str(fold_index)
734734
dictionary.add_variable_from_spec(name=name, type="Numerical", used=False)
735-
dictionary.get_variable(name).set_rule(
735+
dictionary.get_variable(name).rule = str(
736736
kh.Rule("NEQ", fold_index_variable, fold_index),
737737
)
738738

tests/test_core.py

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1890,7 +1890,7 @@ def test_dictionary_accessors(self):
18901890
name="fresh_one", type="Structure"
18911891
)
18921892
# rule must be Rule object
1893-
with self.assertRaisesRegex(TypeError, "'rule'.*Rule"):
1893+
with self.assertRaisesRegex(TypeError, "'rule'.*'str'"):
18941894
dictionary.add_variable_from_spec(
18951895
name="fresh_one", type="Categorical", rule={}
18961896
)
@@ -1904,7 +1904,9 @@ def test_dictionary_accessors(self):
19041904
name="fresh_one",
19051905
type="Categorical",
19061906
meta_data={"a": 1, "b": 2},
1907-
rule=kh.Rule("Ceil", kh.Rule("Product", 3, kh.Rule("Random()"))),
1907+
rule=str(
1908+
kh.Rule("Ceil", kh.Rule("Product", 3, kh.Rule("Random()")))
1909+
),
19081910
)
19091911
self.assertEqual(
19101912
2,
@@ -1913,7 +1915,7 @@ def test_dictionary_accessors(self):
19131915
)
19141916
self.assertEqual(
19151917
"Ceil(Product(3, Random()))",
1916-
repr(dictionary.get_variable("fresh_one").get_rule()),
1918+
dictionary.get_variable("fresh_one").rule,
19171919
"Variable rule must be set correctly",
19181920
)
19191921
variable_rule = kh.Rule(verbatim="Ceil(Product(3, Random()))")
@@ -1965,7 +1967,7 @@ def test_dictionary_accessors(self):
19651967
block_rule = kh.Rule("SomeBlockCreatingRule")
19661968
with self.assertRaises(TypeError):
19671969
dictionary_copy.get_variable_block(block.name).rule = block_rule
1968-
dictionary_copy.get_variable_block(block.name).set_rule(block_rule)
1970+
dictionary_copy.get_variable_block(block.name).rule = str(block_rule)
19691971
self.assertEqual(block, dictionary_copy.get_variable_block(block.name))
19701972
removed_block = dictionary_copy.remove_variable_block(
19711973
block.name,
@@ -2020,13 +2022,13 @@ def test_dictionary_accessors(self):
20202022
2,
20212023
kh.Rule("SomeEmbeddedRule()"),
20222024
)
2023-
dictionary_copy.get_variable(variable_name).set_rule(some_rule)
2025+
dictionary_copy.get_variable(variable_name).rule = str(some_rule)
20242026
self.assertEqual(
20252027
dictionary_copy.get_variable(variable_name).rule,
20262028
repr(some_rule),
20272029
)
20282030
self.assertEqual(
2029-
repr(dictionary_copy.get_variable(variable_name).get_rule()),
2031+
dictionary_copy.get_variable(variable_name).rule,
20302032
repr(some_rule),
20312033
)
20322034
some_reference_rule = kh.Rule(
@@ -2039,15 +2041,15 @@ def test_dictionary_accessors(self):
20392041
),
20402042
is_reference=True,
20412043
)
2042-
dictionary_copy.get_variable(variable_name).set_rule(
2044+
dictionary_copy.get_variable(variable_name).rule = str(
20432045
some_reference_rule
20442046
)
20452047
self.assertEqual(
20462048
dictionary_copy.get_variable(variable_name).rule,
20472049
repr(some_reference_rule),
20482050
)
20492051
self.assertEqual(
2050-
repr(dictionary_copy.get_variable(variable_name).get_rule()),
2052+
dictionary_copy.get_variable(variable_name).rule,
20512053
repr(some_reference_rule),
20522054
)
20532055
for variable_block_index, variable_block_name in enumerate(
@@ -2062,31 +2064,21 @@ def test_dictionary_accessors(self):
20622064
2,
20632065
kh.Rule("SomeEmbeddedRule()"),
20642066
)
2065-
dictionary_copy.get_variable_block(variable_block_name).set_rule(
2067+
dictionary_copy.get_variable_block(variable_block_name).rule = str(
20662068
some_rule
20672069
)
20682070
self.assertEqual(
20692071
dictionary_copy.get_variable_block(variable_block_name).rule,
20702072
repr(some_rule),
20712073
)
20722074
self.assertEqual(
2073-
repr(
2074-
dictionary_copy.get_variable_block(
2075-
variable_block_name
2076-
).get_rule()
2077-
),
2075+
dictionary_copy.get_variable_block(variable_block_name).rule,
20782076
repr(some_rule),
20792077
)
2080-
some_reference_rule = kh.Rule(
2081-
"some_reference_operand_for_variable block"
2082-
+ variable_block_index * "i",
2083-
3,
2084-
is_reference=True,
2085-
)
2086-
with self.assertRaises(ValueError):
2087-
dictionary_copy.get_variable_block(
2088-
variable_block_name
2089-
).set_rule(some_reference_rule)
2078+
with self.assertRaises(TypeError):
2079+
dictionary_copy.get_variable_block(variable_block_name).rule = (
2080+
some_rule
2081+
)
20902082

20912083
def test_dictionary_rule_construction(self):
20922084
"""Tests the Rule construction and serialization"""

0 commit comments

Comments
 (0)