π Strategies#
The galactic.algebras.convex.strategies.core module defines the foundational
building blocks of strategies.
It provides:
the
Strategyprotocolthe
BasicStrategyprotocol 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
Now add the top concept:
lattice.extend([top])
diagram
And gradually extend with strategy results:
Adding 2 concepts from NaiveStrategy
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
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 measureSelectiveStrategy- keep only concepts within a ratio of the best measureCreating custom measures for domain-specific quality evaluation