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
isNone
, buttype
is a list. -attr
exists, buttype
is not a list. - The format ofdesc
does not match the presence ofattr
.
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):
- For each domain (total count specified by
domain
): - For each dimension (count specified by
dim
): - For each data source in
source
: Select
amount[k]
items fromsource[k]
followingorder
andduplicate
rules
- For each data source in
- For each dimension (count specified by
Verify dimension-level conditions (
dim_cond
and custom conditions with scope=’dim’)
- For each domain (total count specified by
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].
- 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
- 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