model package

Submodules

model.template module

class model.template.DefinedSymbol(*, source: List[str], attr: List[str] | None = None, type: str | List[str], desc: str | List[str] | None = None)[source]

Bases: BaseModel

Base class for defining symbol templates used in puzzle generation.

This class provides the fundamental structure for creating basic symbol elements in puzzles.

Note: The base data structure is a dictionary, where the keys are the symbol names (or tuples when len(source) > 1) and the values are the Z3 symbols.

If only the list of Z3 symbols are needed, use <dict_name>.to_list() or list(<dict_name>) method to convert the dictionary values to a list.

attr: List[str] | None

List of symbol attributes defining additional characteristics.

Examples

  • [“color”, “size”] indicates the symbol has color and size attributes.

Notes

  • When None (default), symbols can be accessed directly via dictionary keys (event[key]).

  • When specified, symbols must be accessed using get() method (event[key].get(‘color’)).

desc: str | List[str] | None

Description template for the symbol.

Examples

  • Single string when no attributes: “Basic proposition symbol”

  • List matching attr length when attributes exist: [“Color description”, “Size description”]

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

source: List[str]

Initialization expressions for the symbol, serving as primary keys.

Examples

  • When length is 1: The primary key is a string.

    Example: source = [“children”] with children = [“Alice”, “Bob”] will generate two symbols with primary keys “Alice” and “Bob”.

  • When length > 1: The primary key is a tuple.

    Example: source = [“children”, “adults”] with children = [“Alice”, “Bob”], adults = [“Chris”] will generate symbols with primary keys (“Alice”, “Chris”) and (“Bob”, “Chris”).

type: str | List[str]

Type definition rules for the symbol.

Valid Values: ‘int’ (default), ‘bool’, ‘float’, and ‘enum’.

Examples

  • Single type string when attr is None: “Bool”

  • Type list matching attr length when attr exists: [“int”, “bool”]

classmethod validate_type_desc_based_on_attr(values)[source]

Validates consistency between attributes, types and descriptions.

Raises:

ValueError

  • attr is None, but type is a list. - attr exists, but type is not a list. - The format of desc does not match the presence of attr.

Examples

>>> DefinedSymbol(source=["A"], attr=["color"], type="Str")  # Raises ValueError
>>> DefinedSymbol(source=["A"], desc=["desc1", "desc2"])  # Raises ValueError
class model.template.DerivedSymbol(*, source: List[str], amount: list | None = None, order: List[bool] | None = None, duplicate: List[bool] | None = None, domain: str | None = None, domain_cond: bool = True, dim: int = 1, dim_cond: list | None = [], custom_cond: list | None = [], formula: str | None = None, desc: str = '')[source]

Bases: BaseModel

Class defining rules for generating derived symbols from existing ones.

The derivation works by first randomly selecting a number of values from the source list and creating new symbols with the selected values.

Selection Process (before optimization):

  1. For each domain (total count specified by domain):
    1. For each dimension (count specified by dim):
      1. For each data source in source:
        • Select amount[k] items from source[k] following order and duplicate rules

    2. Verify dimension-level conditions (dim_cond and custom conditions with scope=’dim’)

  2. Verify domain-level conditions (domain_cond and custom conditions with scope=’domain’)

Example

"max_num": {
    "source": ["range(p_num)", "range(2, 11)"],
    "domain": "3",
    "custom_cond": [{
        "scope": "domain",
        "fields": [0, 1],
        "constraint": "lambda l: all([item[0][1][0] < money // prices[item[0][0][0]] for item in l])"
    }],
    "formula": "num[names[_sym[0]]] <= _sym[1]",
    "desc": "The maximum number of Award {names[_sym[0]]} that a person can take is {_sym[1]}."
}

This configuration will:

  • Generate 3 symbols (domain = 3)

  • Each symbol has 1 dimension (default dim = 1)

  • Each dimension uses 2 data sources (len(source) = 2)

  • The first source provides integers from 0 to p_num - 1

  • The second source provides integers from 2 to 10

  • Selects 1 item from each source (default when amount is None)

  • Applies custom domain-level constraint on the selected values

amount: list | None

Number of items selected from each data source.

Examples

  • [“2”, “1”]: Select 2 items from first source, 1 from second

  • None: Select exactly 1 item from each source (default)

Note

  • Length must match source

  • Each value must be a string (literal or variable name)

custom_cond: list | None

Custom constraint dictionaries containing:

  • scope: Application level (‘domain’/’dim’)

  • fields: List of field indices from source

  • constraint: Constraint logic expression. Must be a valid Python lambda function string.
    • When scope=”domain”, the input is a 4-dimensional list, where the selected values can be fetched by l[domain_index][dim_index][source_index (in ‘fields’)][amount_index].

    • When scope=”dim”, the input is a 3-dimensional list, where the selected values can be fetched by l[dim_index][source_index (in ‘fields’)][amount_index].

desc: str

Symbol collection description text for puzzle generation.

dim: int

Number of dimensions for the derived symbol.

Example

  • 2 generates two-dimensional symbol matrix (useful for statements with multiple clauses).

dim_cond: list | None

Inter-dimensional constraints (list of conditions).

Example

  • [[0, 1], [2]] means values from source[0] and source[1] cannot be identical at the same time AND values from source[2] cannot be identical.

Note

  • Cannot contain duplicate indices.

Default:
  • [[0, 1, …, len(source)]] (i.e., all selections must be different in at least one source).

domain: str | None

Total number of selections.

Examples

  • “5”: Make 5 selections from the data sources

  • “n”: Use variable n to determine count

Note

Must be a string representing a literal or variable name

domain_cond: bool

Global repetition rule for symbol selection:

Controls whether identical symbol combinations are allowed:

  • True: Disallow identical combinations

  • False: Allow duplicates

Default:

True (no identical combinations)

duplicate: List[bool] | None

Selection repetition rules per data source during selection.

Controls whether duplicates are allowed:

  • True: Allow duplicate selections

  • False: Disallow duplicates

Default:

All False (no duplicates allowed)

Note

Length must match source

formula: str | None

Symbol generation formula using Python syntax.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

order: List[bool] | None

Permutation configuration per data source during selection.

Controls whether selection order matters:

  • True: Permutation (order matters)

  • False: Combination (order doesn’t matter)

Default:

All True (order matters for all sources)

Note

Length must match source

source: List[str]

Data sources for the selection.

Each element must be a string representing either:

  • A stringified list of literals (e.g., ‘[True, False]’)

  • A variable name containing the data

class model.template.DerivedSymbols(*, total: str, templates: List[DerivedSymbol])[source]

Bases: BaseModel

Container for multiple derived symbol templates with random counts.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

templates: List[DerivedSymbol]

List of symbol templates for generation.

total: str

Total number of symbols to generate.

class model.template.DynamicCondition(*, formula: str, desc: str | None = None, source: list, amount: list | None = None, order: List[bool] | None = None, duplicate: List[bool] | None = None, domain: str | None = None, domain_cond: bool = True, custom_cond: list | None = [])[source]

Bases: StaticCondition

Extended constraints with multi-dimensional parameters.

amount: list | None

Number of selections per data source. (same format as DerivedSymbol)

custom_cond: list | None

Custom constraints (same format as DerivedSymbol).

domain: str | None

Total condition count. Must be a range string “[min, max]”. If None, one condition will be generated.

Example

  • “[1, 5]”: Generate between 1 and 5 conditions.

domain_cond: bool

Global repetition rule.

duplicate: List[bool] | None

Repetition rule configuration (default all False).

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

order: List[bool] | None

Permutation configuration (default all True).

source: list

Data sources (same format as DerivedSymbol).

class model.template.Optimize(*, type: str, formula: str)[source]

Bases: BaseModel

Optimization target definition (for optimization problems only).

formula: str

Formula to optimize.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

type: str

Optimization type (“minimize” or “maximize”).

class model.template.PostGen(*, post_gen_vars: Dict[str, str] | None = None, post_gen_conditions: Dict[str, StaticCondition] | None = None)[source]

Bases: BaseModel

Initialization after computing the problem solution for the first time. (Applicable for scenarios where parameters in the actual problem need to be computed using z3)

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

post_gen_conditions: Dict[str, StaticCondition] | None

New constraints to add after initial solution.

Key: The new constraint name.

Value: A string of the formula for the constraint.

post_gen_vars: Dict[str, str] | None

Extracting the values of symbols from _sol (the selected solution) as new variables.

Key: The new variable name.

Value: The expression to compute the variable value, which can be a string of a Python expression.

class model.template.PuzzleTemplate(*, custom_operator: Dict[str, str] | None = None, variables: Dict[str, Variable], symbols: Dict[str, DefinedSymbol | DerivedSymbols | DerivedSymbol] | None = None, conditions: Dict[str, StaticCondition | DynamicCondition] | None = None, calc_solution: bool = True, max_solution: int = 6000, post_generation: PostGen | None = None, optimize: Optimize | None = None, queries: Dict[str, QuerySelectionWithMultipleTemplates | QuerySelectionWithSingleTemplate | Query] | None = None, desc: str)[source]

Bases: BaseModel

Main puzzle template structure integrating all components.

calc_solution: bool

Whether to compute solutions (default True).

conditions: Dict[str, StaticCondition | DynamicCondition] | None

Dictionary of conditions.

custom_operator: Dict[str, str] | None

Dictionary of custom operators.

Key: Operator name.

Value: Python expression string defining the operator OR the path to a Python file containing the operator definition.

Example: {“double”: “lambda x: x * 2”, “reformat”: “customs/mathexpr_generator.py”}

desc: str

Overall template description for puzzle introduction.

max_solution: int

Maximum number of solutions to generate.

Note

  • Here, “solution” refers to a valid configuration of all symbols that satisfies all constraints. It does NOT mean the number of valid answers to the final question.

  • If the number of solutions exceeds this limit, the solver will stop and raise an exception.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

optimize: Optimize | None

Optimization target (for optimization problems only).

post_generation: PostGen | None

Post-generation configuration including:

  • post_gen_vars: New variables from solutions

  • post_gen_conditions: Additional constraints

queries: Dict[str, QuerySelectionWithMultipleTemplates | QuerySelectionWithSingleTemplate | Query] | None

Dictionary of question definitions.

symbols: Dict[str, DefinedSymbol | DerivedSymbols | DerivedSymbol] | None

definition).

Type:

Dictionary of symbol definitions (name

variables: Dict[str, Variable]

definition).

Type:

Dictionary of variable definitions (name

class model.template.Query(*, desc: str, ans_formula: str, ans_text: str, ans_assertion: str | None = 'len(_solutions) == 1')[source]

Bases: QueryBase

Open-ended question definition.

ans_assertion: str | None

Assertion for answer validation.

ans_formula: str

Answer generation formula.

ans_text: str

Answer text format.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class model.template.QueryBase(*, desc: str)[source]

Bases: BaseModel

Base class for question definitions.

desc: str

Question description text (may contain placeholders).

Example: “Which option satisfies the condition?”

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class model.template.QuerySelectionBase(*, desc: str, query_type: str = 'single_choice', select_type: bool = True, opt_num: int | None = 4)[source]

Bases: QueryBase

Multiple-choice question definition.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

opt_num: int | None

Total number of options to present (default 4).

query_type: str

Question type:

  • ‘single_choice’: Single correct answer

  • ‘multiple_choice’: Multiple correct answers

select_type: bool

Whether to select the correct or incorrect option(s):

  • True: Select the correct option(s)

  • False: Select the incorrect option(s)

class model.template.QuerySelectionTemplate(*, source: list, amount: list | None = None, order: List[bool] | None = None, duplicate: List[bool] | None = None, cond: str = 'any', opt_formula: str, opt_text: str | None = None, custom_cond: list | None = [])[source]

Bases: BaseModel

Template for multiple-choice options.

The option generation process works by randomly selecting a number of values from the source list to create options.

amount: list | None

Number of selections per source. (Same format as DerivedSymbol.amount)

cond: str

Constraint scope:

  • ‘any’: At least one solution satisfies

  • ‘all’: All solutions satisfy

custom_cond: list | None

Custom constraints (same format as DerivedSymbol.custom_cond).

duplicate: List[bool] | None

Repetition rule configuration. (default all False)

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

opt_formula: str

Option correctness evaluation expression.

Example: “x % 2 == 0”

opt_text: str | None

Option display template (may contain placeholders).

Example: “{_opt[0][0]}”

Note

  • Automatically prefixed with ABCD, no need to include in string.

order: List[bool] | None

Permutation configuration (default first dimension False, others True).

source: list

Data source. (Same format as DerivedSymbol.source)

class model.template.QuerySelectionWithMultipleTemplates(*, desc: str, query_type: str = 'single_choice', select_type: bool = True, opt_num: int | None = 4, templates: List[QuerySelectionTemplate])[source]

Bases: QuerySelectionBase

Multiple templates for multiple-choice questions.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

templates: List[QuerySelectionTemplate]

List of option templates.

class model.template.QuerySelectionWithSingleTemplate(*, source: list, amount: list | None = None, order: List[bool] | None = None, duplicate: List[bool] | None = None, cond: str = 'any', opt_formula: str, opt_text: str | None = None, custom_cond: list | None = [], desc: str, query_type: str = 'single_choice', select_type: bool = True, opt_num: int | None = 4)[source]

Bases: QuerySelectionBase, QuerySelectionTemplate

Single template for multiple-choice questions.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class model.template.StaticCondition(*, formula: str, desc: str | None = None)[source]

Bases: BaseModel

Base constraint definition for puzzle rules.

desc: str | None

Natural language description for puzzle text generation.

Example: “Sum of two numbers must be less than 10”

formula: str

Constraint logic expression using Python syntax.

Example: “x + y < 10”

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class model.template.Variable(*, type: str | None = None, domain: str | None = None, formula: str | None = None)[source]

Bases: BaseModel

Unified class for defining variables in puzzles.

This class supports two mutually exclusive ways of defining variables:

  • By specifying type and domain.

  • By providing a custom formula in formula.

domain: str | None

The domain of the variable.

Example

  • “[1, 10]”: Represents integers from 1 to 10.

  • “[True, False]”: Represents boolean values.

  • “[‘red’, ‘blue’, ‘green’]”: Represents a selection from the given options.

formula: str | None

The formula used to initialize the variable.

Example

  • “randint(1,6) + randint(1,6)”: Represents the sum of two dice rolls.

Notes

  • Custom operators defined in the custom_operator field of the puzzle template can be used in the formula.

  • If formula is defined, type and domain must not be defined.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

type: str | None

The type of the variable.

Example

  • “int”: Represents an integer variable.

  • “bool”: Represents a boolean variable.

validate_exclusive_fields()[source]

Validates the mutually exclusive constraints between fields.

Raises:

ValueError

  • If both formula and type/domain are defined. - If formula is not defined, but type or domain is missing.

Example

>>> DefinedVar(formula="x+y", type="int")  # Raises ValueError
>>> DefinedVar(type="int")  # Raises ValueError (missing `domain`)
>>> DefinedVar(type="int", domain="[1, 10]")  # Valid
>>> DefinedVar(formula="randint(1,6)")  # Valid

Module contents