A systematic process for generating realistic personas based on cognitive rules, attribute relationships, and demographic constraints. This system creates consistent, believable character profiles for simulation or gaming environments, with a particular focus on transaction scenarios.
DTS is a sampling algorithm that deliberately inverts MCMC principles to achieve controlled divergence in constrained spaces. While MCMC and VAEs converge toward minimum energy states, DTS systematically explores the valid state space while maintaining constraint satisfaction. The algorithm draws inspiration from statistical physics concepts of entropy maximization, applying them to generate diverse but valid samples within defined constraint boundaries.
- Creates realistic personas using rules + DTS optimization
- Enforces hard constraints (e.g., "doctors need PhDs")
- Optimizes soft relationships (e.g., "risk tolerance affects investment style")
graph TD
A[Hard Rules] -->|Enforces| B[Basic Generation]
B -->|Seeds| C[DTF Refinement]
C -->|Validates| A
%% Styling
classDef state fill:#000,stroke:#fff,stroke-width:2px,font-family:Comic Sans MS,font-weight:bold
classDef decision fill:#000,stroke:#fff,stroke-width:2px,font-family:Comic Sans MS,font-weight:bold
classDef process fill:#000,stroke:#fff,stroke-width:2px,font-family:Comic Sans MS,font-weight:bold
class Start,Complete state
class Diverge,Valid,Diverse decision
class Root,Spawn,Store process
- 30% Deterministic (Hard rules that never break)
- 20% Random (Initial generation within rules)
- 50% MCMC (Statistical optimization)
Input:
role: "Software Engineer"
rules:
- needs: ["Bachelor's", "Master's", "PhD"]
- age: >= 22
- income: [70k-200k]
Output:
# Hard Rules (Never Break)
education: "Master's"
age: 28
income: 95k
# MCMC Optimized (Statistically Refined)
risk_tolerance: 72%
personality:
openness: 0.8
conscientiousness: 0.7
investment_style: "Growth-focused"- Rules are never broken
- Initial state is randomly valid
- DTF makes it realistic
- More coherent than pure random
- More diverse than pure rules
The base algorithm follows this pattern:
def wander_tree(initial_state, constraints, relationships):
current_node = initial_state
exploration_tree = []
while not sufficient_diversity:
# Generate divergent child node
child = spawn_child_node(current_node)
# Check validity against constraints
if validate_constraints(child, constraints):
# Calculate validity score
score = calculate_validity(child, relationships)
# Accept with probability proportional to validity
if random() < min(1.0, score/current_score):
current_node = child
exploration_tree.append(child)-
Inverted Acceptance Probability
- Traditional MCMC:
P(accept) = min(1, π(x')/π(x)) - DTS:
P(accept) = min(1, V(x')/V(x))where V is validity score - Forces exploration while maintaining realistic bounds
- Traditional MCMC:
-
Adaptive Node Spawning
num_modifications = max(1, min(4, int(1 + 3 * (1 - recent_acceptance_rate))))
attributes_to_modify = weighted_sample(attributes, size=num_modifications)- Relationship Preservation
- Uses weighted attribute relationships instead of transition kernels
- Maintains statistical validity while allowing divergence
- Handles both categorical and continuous attributes
- Constraint Management
def validate_constraints(node, constraints):
# Hard constraints (e.g., education requirements)
if not check_hard_constraints(node):
return False
# Relationship constraints
return check_relationship_validity(node)-
VS. MCMC
- Actively promotes diversity rather than convergence
- Maintains validity through constraints rather than distribution sampling
- Better handles mixed categorical/continuous spaces
-
VS. VAEs/GANs
- No training required
- Explicit constraint handling
- Deterministic validity checks
- No mode collapse issues
-
VS. Rejection Sampling
- More efficient in high-dimensional constrained spaces
- Maintains attribute relationships
- Guided exploration rather than blind sampling
The core components work together as follows:
class DivergentTreeSampler:
def spawn_child_node(self, parent_node):
# Select attributes to modify based on relationships
attributes = select_weighted_attributes()
# Generate modifications that promote divergence
modifications = generate_divergent_changes(attributes)
# Apply while maintaining minimal validity
return apply_modifications(parent_node, modifications)
def validity_score(self, node):
# Calculate relationship preservation
relationship_score = calculate_relationship_score(node)
# Calculate constraint satisfaction
constraint_score = calculate_constraint_score(node)
return combine_scores(relationship_score, constraint_score)While DTS doesn't guarantee convergence to a target distribution (by design), it provides:
- Constraint Satisfaction: All generated samples meet hard constraints
- Relationship Preservation: Maintains specified attribute correlations
- Diversity Maximization: Actively explores valid state space
- Statistical Validity: Generated populations match specified distributions
- Time Complexity: O(n * c * r) where:
- n = number of attributes
- c = number of constraints
- r = number of relationships
- Space Complexity: O(t) where t = size of exploration tree
- Acceptance Rate: Typically 30-70% depending on constraint strictness
Particularly suited for:
- Generating diverse but realistic personas
- Exploring constrained possibility spaces
- Maintaining complex attribute relationships
- Scenarios requiring explicit constraint satisfaction
flowchart TB
Start([Initial Valid State]) --> Root[Root Node]
Root --> Diverge{Diverge?}
Diverge -->|Yes| Spawn[Spawn Child Node]
Spawn --> Valid{Valid?}
Valid -->|Yes| Store[Store Node]
Store --> Diverse{Enough<br>Diversity?}
Diverse -->|No| Diverge
Valid -->|No| Diverge
Diverse -->|Yes| Complete([Complete])
%% Styling
classDef state fill:#000,stroke:#fff,stroke-width:2px,font-family:Comic Sans MS,font-weight:bold
classDef decision fill:#000,stroke:#fff,stroke-width:2px,font-family:Comic Sans MS,font-weight:bold
classDef process fill:#000,stroke:#fff,stroke-width:2px,font-family:Comic Sans MS,font-weight:bold
class Start,Complete state
class Diverge,Valid,Diverse decision
class Root,Spawn,Store process
Basic Usage:
# Generate 100 personas with default settings
python persona_generator.py
# Generate 500 personas with more iterations for better diversity
python persona_generator.py --num-personas 500 --iterations 2000
# Generate personas without using DTS (simpler but less diverse)
python persona_generator.py --no-wandering --num-personas 100Common Parameters:
--num-personas N Number of personas to generate (default: 100)
--iterations N Number of DTS iterations (default: 1000)
--burn-in N Initial iterations to discard (default: 100)
--exploration-rate X Rate of exploration (0-1, default: 0.1)
--output-dir PATH Output directory (default: 'output')
--debug Enable detailed logging
--skip-visualizations Skip generating visualizationsExpected Outputs:
output/
├── [timestamp]/
│ ├── personas/ # Generated personas
│ │ ├── [Name1].yaml
│ │ ├── [Name1].json
│ │ └── [Name1].md
│ ├── visualizations/ # Visual analysis
│ │ ├── diagnostics/
│ │ │ ├── wandering_diagnostics_1.png
│ │ │ └── wandering_diagnostics_2.png
│ │ └── personality/
│ │ └── personality_analysis.png
│ └── statistics/ # Population analysis
│ └── population_statistics.md
Example Command with All Options:
python persona_generator.py \
--num-personas 200 \
--iterations 1500 \
--burn-in 150 \
--exploration-rate 0.15 \
--output-dir "my_personas" \
--debugThis will generate:
- 200 diverse personas
- Diagnostic visualizations showing the exploration process
- Personality trait analysis across generations
- Comprehensive population statistics
- Detailed generation logs (due to --debug)
The script will maintain distributions of:
- Demographics (age, gender)
- Education levels
- Occupations
- Income ranges
- Personality traits
- Other defined attributes
While ensuring all relationships (like education-occupation, age-retirement, income-occupation) remain realistic according to the configured rules.
attribute_options.yaml: This is the primary configuration file that defines all possible attributes and their valid values/ranges. It contains:
- Individual attributes with either ranges or discrete options:
# Continuous range attributes
age:
range: '18-80'
income:
range: '20000-300000'
# Discrete option attributes
gender:
options:
- value: Male
distribution: 49
- value: Female
distribution: 49
- value: Non-binary
distribution: 2- Occupation definitions with detailed constraints:
occupation:
options:
- value: Software Developer
total_employment: 1656880
valid_education: [Some College, Bachelor's Degree, Master's Degree]
valid_income_range: [92589, 171951]
min_age: 22
retirement_age: 65attribute_relationships.yaml: Defines how different attributes influence each other through weighted relationships:
age:
relationships:
- secondary_attribute: education_level
weight: 0.8
conditions: [age<22]
- secondary_attribute: occupation
weight: 0.7Key concepts:
weight: Strength of relationship (-1 to 1)conditions: When relationships apply- Can include specific value assignments
persona_template.yamlandpersona_template.md: These define how the generated personas are formatted:
template: >
name: {name}
description: >
{name} is a {age}-year-old {gender}...Roles in the Script:
- AttributeOptions class:
- Loads attribute_options.yaml
- Validates configurations
- Provides methods to get random valid options
- Enforces constraints between attributes
- AttributeRelationships class:
- Loads attribute_relationships.yaml
- Calculates weighted influences between attributes
- Ensures generated personas maintain realistic relationships
- PersonaGenerator class:
- Uses both configurations to generate valid personas
- Applies templates for output formatting
- Maintains statistical validity and diversity
- DivergentTreeSampler class:
- Uses the configurations to explore the valid state space
- Maintains constraint satisfaction while maximizing diversity
- Uses relationships to guide exploration
The YAML configurations effectively create a framework of rules and relationships that guide the generation of realistic, diverse, yet constrained personas. The script uses these to create statistically valid populations while maintaining individual attribute relationships and constraints.
