πŸ“˜ Strategies#

The galactic.algebras.convex.strategies.core module defines the foundational building blocks of strategies.

It provides:

  • the Strategy protocol

  • the BasicStrategy protocol whose results are computed from the characteristics of items. Each subclass implements a specific way to compute the strategy result from the characteristics of an item using the _call() method.

Setup#

First, let’s set up a context with a dataset and descriptions:

from galactic.algebras.concept.core import ItemUniverse
from galactic.algebras.convex.characteristics.core import Component, Integer
from galactic.algebras.convex.descriptions.core import (
    Concept,
    PredicateUniverse,
    Context,
    GaloisConnection,
)
from galactic.algebras.convex.descriptions.examples.arithmetic.core import (
    MultipleDescription,
    DivisorDescription,
)

# Create a context with a small dataset
dataset = [36, 48, 16, 32, 12, 24]
domain = ItemUniverse(dataset)
characteristic = Integer(components=(Component(),))
descriptions = [
    DivisorDescription(space=(characteristic,)),
    MultipleDescription(space=(characteristic,)),
]
co_domain = PredicateUniverse(*descriptions)
context = Context(domain, co_domain)
connection = GaloisConnection(context)

# Get the top concept
top = Concept(connection)
print(f"Top concept contains {len(top.extent)} items")
Top concept contains 6 items

NaiveStrategy#

The NaiveStrategy generates direct predecessors of a concept by computing singletons and removing one element at a time. It is the most fundamental strategy and works well when description spaces are convex.

from galactic.algebras.convex.strategies.core import NaiveStrategy

# Create a naive strategy
naive_strategy = NaiveStrategy(descriptions=descriptions)

# Apply it to the top concept
concepts = list(naive_strategy(top))
print(f"NaiveStrategy generated {len(concepts)} sub-concepts")

# Display the results
for index, concept in enumerate(concepts):
    print(f"Concept {index + 1}: ", end="")
    print(f"{len(concept.extent)} items - {[item.value for item in concept.extent]}")
NaiveStrategy generated 2 sub-concepts
Concept 1: 5 items - [48, 16, 32, 12, 24]
Concept 2: 5 items - [36, 48, 16, 12, 24]

CompositeStrategy#

The CompositeStrategy applies multiple inner strategies sequentially and yields all results. It is useful when you want to combine different strategy approaches.

You can create a CompositeStrategy by passing a tuple of inner strategies:

from galactic.algebras.convex.strategies.core import CompositeStrategy, NaiveStrategy

strategy = NaiveStrategy(descriptions=descriptions)
multiple = CompositeStrategy(strategies=(strategy, strategy))

# Apply it to the top concept
concepts = list(multiple(top))
print(f"CompositeStrategy produces {len(concepts)} sub-concepts from the strategy")

# Display the results
for index, concept in enumerate(concepts):
    print(f"Concept {index + 1}: ", end="")
    print(f"{len(concept.extent)} items - {[item.value for item in concept.extent]}")
CompositeStrategy produces 4 sub-concepts from the strategy
Concept 1: 5 items - [48, 16, 32, 12, 24]
Concept 2: 5 items - [36, 48, 16, 12, 24]
Concept 3: 5 items - [48, 16, 32, 12, 24]
Concept 4: 5 items - [36, 48, 16, 12, 24]

RecursiveStrategy#

The RecursiveStrategy recursively applies an inner strategy to generate the entire lattice of concepts. It maintains a heap queue to traverse concepts in order of decreasing support.

from galactic.algebras.convex.strategies.core import RecursiveStrategy

# Create a recursive strategy wrapping the naive strategy
recursive_strategy = RecursiveStrategy(strategies=(naive_strategy,))

# Apply it to generate the full lattice
concepts = list(recursive_strategy(top))
print(f"RecursiveStrategy generated {len(concepts)} concepts total")

# Display extent sizes
extent_sizes = [len(concept.extent) for concept in concepts]
print(f"Extent sizes: {extent_sizes}")
RecursiveStrategy generated 22 concepts total
Extent sizes: [6, 5, 5, 4, 4, 4, 3, 3, 3, 3, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 0]

You can also add a support threshold to stop recursion:

# Create a recursive strategy with a minimum support threshold
recursive_with_support = RecursiveStrategy(
    strategies=(naive_strategy,),
    support=3,  # Stop when extents smaller than 3
)

# Apply it
concepts = list(recursive_with_support(top))
print(f"With support >= 3: generated {len(concepts)} concepts")

# Display extent sizes
extent_sizes = [len(concept.extent) for concept in concepts]
print(f"Extent sizes: {extent_sizes}")
With support >= 3: generated 10 concepts
Extent sizes: [6, 5, 5, 4, 4, 4, 3, 3, 3, 3]

Visualising Concept Lattices#

Strategies are particularly useful when visualizing concept lattices. Let’s extend a lattice and visualize it:

from galactic.algebras.concept.core import ExtensibleLattice
from galactic.algebras.concept.viewer import ConceptRenderer
from galactic.algebras.poset.viewer import HasseDiagram

# Create an empty lattice with a renderer
lattice = ExtensibleLattice()
diagram = HasseDiagram(lattice, domain_renderer=ConceptRenderer(lattice))
diagram
../_images/b559f92db82c2ff94fab394281c86c1d53fa318ffc2e6aa697db72f16885fe01.svg

Now add the top concept:

lattice.extend([top])
diagram
../_images/edb9e5235123de6f1ec7c808028e88eadd68d34abf9c6f96c96feaa08dcd928b.svg

And gradually extend with strategy results:

# Get some concepts from the naive strategy
strategy = NaiveStrategy(descriptions=descriptions)
concepts = list(strategy(top))
print(f"Adding {len(concepts)} concepts from NaiveStrategy")
lattice.extend(concepts)
diagram
Adding 2 concepts from NaiveStrategy
../_images/76474489add89c39aed8c19bb509237e3043345981cfa956eca3783d8906dd23.svg

If you want the full lattice, use the recursive strategy:

# Clear and rebuild with full recursive lattice
lattice2 = ExtensibleLattice()
diagram2 = HasseDiagram(lattice2, domain_renderer=ConceptRenderer(lattice2))

# Add all concepts recursively
strategy = RecursiveStrategy(
    strategies=(NaiveStrategy(descriptions=descriptions),),
)
concepts = list(strategy(top))
lattice2.extend([top] + concepts)

print(f"Full lattice contains {len(concepts)} concepts")
diagram2
Full lattice contains 22 concepts
../_images/59b0c84f268e2a9eb497f764aaaedc056da50b6fdbae1b0049a5a84f64f69829.svg

Measure-Driven Strategies#

For advanced filtering and ranking of concepts based on quality metrics, see the πŸ“˜ Measures tutorial. It covers:

  • FilteringStrategy - filter concepts by a threshold on a measure

  • SelectiveStrategy - keep only concepts within a ratio of the best measure

  • Creating custom measures for domain-specific quality evaluation