The concept tree is our main data structure where we store the knowledge learned from the input so far. It is built incrementally by each new line of the input. In a sense it is similar to a finite automaton, as it takes as input a string and processes the string through its nodes. However, there are some major differences: the tree is dynamically built as the input is processed, and the accept and reject nodes are not fixed until the last input line is read.
Since the concept tree is a rooted tree, we always start with a root node. The first line of input is then processed. The tree allows a fixed number of node types to be inserted into it. These node types actually correspond to atomic concepts which cannot be divided into simpler concepts. This set of atomic concept nodes is predetermined by us, and it is not necessarily a complete or sufficient set. Indeed finding a complete and sufficient atomic concept set is an interesting problem; however, we do not try to solve this problem in this study. Once we process a line of input, it turns into multiple nodes in the tree, and potentially multiple paths. The leaf nodes can be positive or negative in polarity. Each path that starts from the root node and ends at a leaf node represents an intermediate level concept and the polarity of the leaf node determines whether that concept can explain the inputs or not. In that sense, the tree contains both concepts that can be used to identify the input concept, as well as concepts that cannot be used. Although a single path as defined above can represent an intermediate concept, multiple paths can be taken as a subtree to represent a more complicated intermediate level concept. For example in
Figure 2, a simple concept tree that stores two concepts can be seen. The path from the root to the left child goes through two nodes: an
s node and an
a node. Although, the details of node types will be later provided, we will shortly tell here that
s stands for “Starts with” and
a stands for “Any”. Together, the path represents a concept of all binary strings that start with a 1, and continue with any string. Similarly, the path that follows the right child from the root represents a concept of all binary strings that “Ends with” a 0. Having these two concepts stored in the tree means the strings in the input all do start with 1 or end with 0.
3.3.1. Formal Definitions
We now formalize the objects introduced above. Let
be the binary alphabet and let
be the set of finite binary strings. To model trimming, we extend the alphabet to
, where
is a placeholder marking a position that has already been consumed and can no longer be matched. A
predicate (atomic concept) is a triple
, where
t is one of the node types of
Table 2,
a is its (possibly empty) character/numeric argument, and
indicates whether
p is a
trimming variant. Each predicate is equipped with two total functions,
where
decides whether the predicate matches the current string and
is the string transformation propagated to the children. For a non-trimming predicate (
), we have
; for a trimming predicate (
),
replaces the matched characters by
(or removes the leading/trailing one), which guarantees that
strictly reduces the number of unconsumed characters and hence that every path terminates. Let
denote the finite set of all predicates obtainable from the eight node types together with their admissible arguments.
A
concept tree is a rooted tree
with root
r, in which every non-root node
carries a predicate
and a status
{
active,
deleted}. For a node
v with ancestor chain
, the string reaching
v when the input
w is processed is obtained by the left-to-right composition of the transforms,
and
v accepts w if every predicate on the chain accepts its corresponding intermediate string, i.e.,
for all
. A root-to-leaf path
thus denotes a
rule , namely the conjunction of its predicates under progressive trimming, and the set of strings it accepts is its
coverage
The concept represented by a set of active leaf paths
is their disjunction, and the induced binary classifier
labels a string positive iff it is covered by at least one selected path:
The training data is a multiset
with
and labels
; write
and
for the positive and negative subsets. A path
is
consistent with
D if
and
. After all data have been processed, the learning objective (formalized in
Section 3.3.4) is to return a
minimum-cost set of active paths
whose coverage includes every positive example and excludes every negative one, preferring fewer and shorter paths in accordance with Occam’s Razor. With this vocabulary the operations of the next section read as follows:
construct grows
V and
E so that
T contains every path consistent with the positive examples observed so far,
destruct sets
deleted for paths invalidated by a negative example, and the filtering step selects
.
3.3.2. Construction
Initially a concept tree is empty except its root node. As we process input data line by line, we mainly do one of two things: either we follow an existing child node or we create new children that match the current string. We then recursively follow each child that matches the current string, as long as there is a matching child and we have not yet hit the tree depth limit.
Some node types have two variants: one which does not modify the input string and one that trims the input string. For example, assume a string of “010” reaches an
e(0) node with trim variant. The node checks whether the string ends with a 0, and in this case it does. The node then replaces the ending 0 with an X, turning the string into “01X” and passes the modified string to its children. An ‘X’ is a placeholder character representing an already processed bit which cannot be processed any further. Therefore, in practice the string is now reduced to “01”. This ensures that the same node types cannot be applied on a string infinitely and execution must halt eventually. In our node visualizations, we will depict the trim variants with a “%” character. For example, in
Figure 3, the ‘LEN[3]%’ node represents the trim variant of the
len node with a parameter of 3. For readability, all concept-tree figures in this paper draw only the
active nodes of the filtered response; the small green square in the lower-right corner of each node is a status indicator marking it active (the implementation additionally tracks
passive and
deleted nodes, which are omitted from the figures).
Once a leaf node is reached and the whole input string is processed, the string turns into a concept in the tree: that is a path from the root node to the leaf node. The same string may end up at different leaf nodes of the tree simultaneously. That is due to the fact that there may be more than one concept that describes the same string. If the input string polarity is positive this means the rule represented by this path can be used to describe the concept that the tree is learning. We call such paths “accepted”. However, if the polarity is negative, then this path conflicts with the concept and should be avoided in describing the concept. We call such paths “rejected”. However, a path that is already marked as rejected may later have new children that produce accepted paths. That is one of the most difficult challenges we have faced in constructing our algorithm, and we solve this problem by sorting the input with respect to polarity in the beginning. This way, we process the positive strings first and negative strings later. This, in turn, prevents us from first labeling a path as rejected and then produce longer paths from it labeled as accepted.
The overall build procedure is given in Algorithm 1. When building the tree, we use two fundamental operations: construct and destruct. construct and destruct operations are given in Algorithms 2 and 3, respectively. We now briefly explain each procedure.
The
build procedure starts with sorting the input strings with respect to their polarity. This step is very crucial as explained above and avoids early deletion of paths that can be used later. Next, we create the root node of the tree and start processing the input line by line. If we receive a positive input, then we only call the
construct procedure. However, for a negative input, we first call the
construct procedure and then the
destruct procedure. Let us now explain these procedures and the reason behind the different behavior based on the polarity of the input.
| Algorithm 1 CTL Build Operation |
- 1:
procedure Build() - 2:
- 3:
CreateRootNode() - 4:
for all in do - 5:
- 6:
- 7:
if then - 8:
Construct(, ) - 9:
else - 10:
Construct(, ) - 11:
Destruct(, ) - 12:
end if - 13:
end for - 14:
end procedure
|
The
construct operation simply extends the tree to accommodate the knowledge in a new input line. Its purpose is to check all possible node type, character argument, numeric argument and trim variations to find combinations that can accept the current input string. Then, these combinations are appended to the current node as new children as long as they do not already exist. Next, the processed input string is recursively sent to the newly created children to be further processed until either the tree becomes full or the string becomes empty. The outline of the
construct operation is presented in Algorithm 2.
| Algorithm 2 Construct Operation |
- 1:
procedure Construct(, ) - 2:
for all t in do - 3:
for all c in do character variants - 4:
for to do ▹ numeric variants, bounded dynamically by input length - 5:
for r in do trim variants - 6:
if then - 7:
if then - 8:
- 9:
else - 10:
- 11:
- 12:
end if - 13:
Construct(, ) - 14:
end if - 15:
end for - 16:
end for - 17:
end for - 18:
end for - 19:
end procedure
|
An important detail concerns the range of the numeric argument
i for the length- and count-based predicates (
len and
has). Rather than iterating over a fixed, hard-coded interval, the loop on line 4 of Algorithm 2 runs from a small constant
(we use
, the smallest threshold that yields a non-trivial length or count predicate) up to the length
of the string currently reaching the node. This bound is principled: a length or count threshold larger than the available string can never be matched, so the dynamic range neither omits any feasible predicate nor wastes work on infeasible ones. Because
shrinks as trimming predicates consume characters along a path, the search space contracts with depth. Consequently the construction adapts automatically to inputs of
any length—in particular, strings of length six or more are handled with no change to the algorithm—while simultaneously avoiding the combinatorial blow-up that a fixed, length-independent upper bound would incur on short residual strings. The empirical effect of this choice on tree size is reported in
Section 4.2.
For completeness, we state the concrete settings and matching convention used throughout. The
dictionary (alphabet) is
, and the character argument
c of the
s,
e, and
has nodes ranges over
. The tree-depth limit is
(the
MAX_LEVEL constant): once a path reaches depth
d it is terminated with an
any node. The numeric argument
i ranges over
as just described. Crucially, every node applies its acceptance test
to the string
reaching it—that is, to the residual produced by the trimming of its ancestors (Equation (
2)), not to the original input. Thus, a
starts-with node nested below a trimming
starts-with node tests the
second character of the original string, which is exactly what lets chained predicates recognize multi-character prefixes, suffixes, and their combinations.
When a negative input is received, we first construct the corresponding paths on the tree as if they were positive, then we call the
destruct procedure, which changes the status of newly created nodes to ‘deleted’. A ‘deleted’ node represents a dead end. We know that the subtree under a deleted node does not contain any accepting nodes. To achieve this, the procedure recursively attempts to reach leaf nodes by following ‘accepting’ nodes. When a leaf node is reached, the status of that leaf node is changed to deleted. Then, the procedure moves up to the parent node by exiting the recursive call. If all children of a parent are now marked as deleted, the status of the parent also becomes ‘deleted’. This process continues up to the root node. If at least one child of a parent is not deleted, then the parent remains active and recursion terminates at that node. The outline of the
destruct operation is given in Algorithm 3.
| Algorithm 3 Destruct Operation |
- 1:
procedure Destruct(, ) - 2:
if and then - 3:
for all in do - 4:
Destruct(, ) - 5:
end for - 6:
if not then - 7:
- 8:
end if - 9:
end if - 10:
end procedure
|
It is worth clarifying precisely what is deleted and why this does not discard correct hypotheses, since marking matched paths as dead ends might appear, at first sight, to throw away information that a more specific rule could later exploit. Two points address this. First, destruct does not delete a path merely because some rule on it matches a negative example; a node becomes deleted only once all of its descendant leaves have been deleted, i.e., only when there is no longer any way to extend the path into a rule that still excludes the offending negative example. Specialization—the very “refinement” one might wish to add under a node that matches a negative example—is therefore not lost: it is realized elsewhere in the tree, by the sibling and cousin paths that construct has already grown in parallel and that exclude the negative example through a different, more specific predicate combination. Because CTL maintains the entire population of consistent hypotheses simultaneously rather than a single clause, “deleting” an over-general path is equivalent to ruling that path out of the disjunction while keeping every more specific alternative alive.
Second, the safety of deletion depends on the positive-before-negative ordering established in
Section 3.2. We state this explicitly.
We make this precise. Fix a dataset
and recall (
Section 3.3.1) that a root-to-leaf path
is
consistent with
D if
and
. Say a leaf path
is
required if it is consistent and some positive example
lies in
but in no other consistent leaf path; the algorithm must not delete a required path.
Proposition 1 (Soundness of deletion under sorted batch processing). Suppose build processes all examples of before any example of . Then (i) no leaf path that is consistent with D is ever marked deleted; consequently (ii) no required path is deleted, and (iii) if any consistent leaf path exists in the hypothesis space, at least one remains active after all examples are processed.
Proof. Because positives precede negatives, at the moment the first negative example is read, construct has already been called on every ; hence for every leaf path present in the tree, fails only if rejects some positive, in which case is not consistent. Now let be any consistent leaf path. By consistency , so for every negative example the leaf of does not accept . Inspecting destruct (Algorithm 3), a node is set to deleted only along the recursion that follows nodes accepting the current , and a leaf is deleted only if it accepts ; since the leaf of accepts no negative, it is never deleted, and an internal node is deleted only when all its children are deleted, so no ancestor of ’s leaf can be deleted while that leaf survives. This proves (i). Claim (ii) is immediate, since a required path is by definition consistent. For (iii), note that no positive example is processed after any negative, so once a node is deleted, no later input adds an accepting descendant to it; thus, deletions are permanent but, by (i), confined to inconsistent paths, and any consistent path that exists is constructed (during the positive phase) and never subsequently deleted. □
This argument is exactly why the sorted, batch regime is required: under an arbitrary (online or interleaved) ordering, a negative example could delete a path before a later positive example reveals that a more specific descendant of that path was needed, and deletion would then be unsafe. Removing the ordering assumption therefore demands an online variant of
destruct that
deactivates rather than permanently deletes, or that re-expands deactivated subtrees on demand; we return to this in
Section 4.3 as the principal avenue for extending CTL beyond batch learning.
3.3.3. Node Types
During construction of the concept tree, we use a fixed number of predetermined node types. We now briefly explain each node type and give examples of use of each.
Our concept tree operates with a total of 8 node types. These node types are: Root(r), Any(a), Not(not), Starts with(s), Ends with(e), Has(has), Length(len), and Equal(eq). Some node types require a specific character as parameter, such as an s node that checks whether the string starts with a specific character. Some node types also require a numeric value as parameter, such as len, that checks whether the length of the string is the given parameter value. The any node is a special node that ends any path. In other words, all leaf nodes are any nodes.
Table 2 displays a summary of all node types along with one negative and one positive example for each. In the last column, it provides the output of the processing of a positive input at this node type, which will then be propagated to its children as input.
We now briefly discuss each node type.
- Root
The r node is the first node of any concept tree. It does not really do anything other than being a placeholder for the algorithm to start.
- Any
The a node is the end of each path in a concept tree. As the name implies, it simply accepts whatever is input to it. This way, we ensure that we do not extend paths unnecessarily if all inputs arriving at a node are positives.
- Not
The
not node is a special node that does not process the input string but rather toggles the accepted parity for its children. For example, regularly an
s[1] node checks whether a string starts with a 1. However, when it is placed under a
not node, it checks whether a string
does not start with a 1.
Figure 3 demonstrates the effect of
not on an input set of binary strings that represent the concept of length being different than 3.
- Starts With
The
s node represents the fundamental concept of starting with a specific character. The character is given as an argument, hence it has two versions: starts with 1 and starts with 0. Additionally it has trim and non-trim variants. The trim variant removes the heading character of the string upon a successful matching. Therefore, in a chain of starts with nodes one can check whether a string starts with longer sequences of characters.
Figure 4 demonstrates the use of starts with nodes in a very simple example. Where the presented concept is strings that start with ‘10’, the obtained tree has a single path containing one
s[1]% and one
s[0]% node. Both nodes are used in their trim variants. Especially the
s[1]% node has to be a trim variant so that the next node can check the second character of the string rather than re-checking the first character.
- Ends With
The
e node represents the fundamental concept of ending with a specific character. This node is very similar to the starts with node, with the only difference being that it works from the end. It has trim and non-trim variants, where the trim variant removes the last character of the string upon a successful matching. Therefore, in a chain of ends with nodes one can check whether a string ends with longer sequences of characters.
Figure 5 demonstrates the use of ends with nodes, where the presented concept is strings that end with ‘01’, the obtained tree has a single path containing one
e[1]% and one
e[0]% node. Both nodes are used in their trim variants. Especially the
e[1]% node has to be a trim variant so that the next node can check the second to last character of the string rather than re-checking the last character.
- Has
The
has node checks whether the input string contains a given character ‘c’ for at least a certain number ‘n’ of times. For example, given the input string ‘10110’,
has[3,0] rejects the string, whereas the input string ‘1001100’ is accepted by the same node. The
has node has a trim and non-trim variant. Similar to the previous node types, the trim version removes all of the matching ‘c’ characters from the string, if there are at least ‘n’ of them.
Figure 6 demonstrates the
has[3,0] node case, which accepts all strings that have at least three 0 s.
- Length
The
len node checks whether the input string is of a certain length ‘n’. For example, given the input string ‘10110’,
len[3] rejects the string, whereas the input string ‘100’ is accepted by the same node. The
len node has a trim and non-trim variant. The trim version simply removes all characters from the string.
Figure 7 demonstrates the
len[3] node case, which accepts all strings that have a length of 3.
- Equals
The
EQ node checks whether the input string has the same number of 1 s and 0 s. For example, given the input string ‘10110’,
eq rejects the string, whereas the input string ‘1001’ is accepted by the same node. The
eq node has a trim and non-trim variant. The trim version simply removes all characters from the string.
Figure 8 demonstrates the
eq node case, which accepts all strings that have an equal number of ones and zeros.
The choice of these particular eight node types is deliberate but not claimed to be canonical: they were selected to cover, with a minimal vocabulary, the basic structural dimensions along which a short binary string can be characterized—
positional information at the two ends (
starts with,
ends with),
counting information (
has, and the relational
equal),
size (
length), and the logical operators needed to combine and close off rules (
not,
any), all anchored at the
root. This set is intentionally small and, we stress,
incomplete: many natural concepts (for example “contains the substring 101”) cannot be expressed with it. Enlarging the vocabulary would let CTL learn a much larger class of intermediate concepts, but at a steep computational price: as the complexity analysis in
Section 4.2 makes precise, each additional atomic predicate (with its trim and non-trim variants) increases the branching factor
P and therefore inflates the
search cost. We therefore deliberately restrict ourselves to this compact set, which is already expressive enough to capture a range of non-trivial intermediate concepts, as the experiments will show. In the next section, we explain how we query a concept tree to obtain the best concept definition.
3.3.4. Filtering a Tree
Once constructed with many inputs, the concept tree stores all kinds of rules (paths) to describe the inputs line by line. Unfortunately, these rules are mostly overlapping: two or more different rules can describe the same input line. Therefore, we have to filter the desired rules from the tree. Our aim here is twofold: pick as few rules as possible and as short rules as possible. We propose, in parallel to the classic Occam’s Razor principle, that the simplest answer is the best answer.
We formalize this filtering step as a weighted set-cover problem. Let
be the set of active leaf paths remaining after construction and destruction, and recall from
Section 3.3.1 that each path
accepts exactly the positive examples in
. By construction every active path is consistent, so
for all
j; the only remaining task is to cover the positives. We must therefore select a subfamily
such that
i.e., every positive example is explained by at least one selected rule. Among all families satisfying (
5) we seek the one minimizing a cost that encodes Occam’s Razor,
where the unit term penalizes the
number of rules and the depth term breaks ties in favor of
shorter rules. We take
in the lexicographic limit
, so that (
6) is exactly the
minimum-cardinality set-cover objective (minimize the number of selected paths) with total depth as a secondary, tie-breaking criterion; we do not tune
as a free parameter. Minimum-cardinality set cover is NP-hard [
36]; the universe to be covered is
and the available sets are the
. Note that overlap between rules (
) is expected and harmless: it simply means a positive example admits several valid explanations, and the objective (
6) resolves the redundancy by keeping the smallest covering subfamily.
Special cases fall out naturally. If some single path covers all of
, then the optimum of (
6) is that path alone (the shortest such, by the depth tie-break); only when no single rule suffices is a genuine disjunction of several paths selected. Because an exact solution is intractable, we approximate the cardinality objective with the classical greedy set-cover heuristic in the analysis of Lund and Yannakakis [
37], which repeatedly selects the path of maximum marginal coverage. Concretely, each candidate path is scored by how many
still-uncovered positives it covers, normalized by how many other active paths also cover those positives (so that rules covering “rare” positives are favored), and the depth tie-break of (
6) is applied within this selection to prefer shorter paths; the chosen path’s covered positives are then removed from the universe and the process repeats. Formally, writing
for the set of positives not yet covered (initially
), each iteration selects
and halts when
; the denominator realizes the same depth tie-break as (
6) (in the limit
it reduces to plain maximum marginal coverage). For a universe of size
, this greedy rule returns a cover of size at most
times the optimum [
37], i.e., it targets the minimum-cardinality term of (
6) with a logarithmic approximation factor rather than minimizing the depth-weighted sum exactly; the depth term enters only as a tie-break, which is adequate for the small rule families that arise in practice. The procedure interacts cleanly with the tree’s hierarchy: scoring and selection range over leaf paths, while the inheritance structure of the tree is used only to enumerate candidate paths and to read off each path’s predicate sequence.
Because the filtering step is an approximation, it is fair to ask whether the choice of heuristic risks selecting an incorrect or unnecessarily large rule set. Two observations bound this risk. First, the approximation only affects
which minimal cover is reported, never
correctness: every candidate path is, by construction, consistent with the data (it accepts all positives reaching it and no negative), so any selected subfamily that covers
classifies the entire training set correctly regardless of the heuristic. The only quantity the heuristic can degrade is parsimony—in the worst case, it could return a cover up to a logarithmic factor larger than the optimum [
37]. Second, we checked this empirically: re-running all 29 tasks with a simpler greedy maximum-coverage selection rule in place of the Lund–Yannakakis rule yielded the
identical final rule set in every one of the 29 cases, with
training accuracy throughout. For the small, low-overlap rule families that arise in this setting, the result is therefore insensitive to the particular approximation used; a systematic study of harder instances where the heuristics diverge is left to future work. We now continue with an example to demonstrate how our concept learner operates on a relatively complicated example.