=== GENERATED PROMPT FOR AGENT ===

# Universal MiniZinc Instance Characteristics Extraction

Create a Python script that extracts instance characteristics from the problem instance 'example.json'. These characteristics will be used by a separate process to determine optimal solver parameters.

## Input Format

# JSON Schema for Fixed Length Error Correcting Codes (FLECC) Problem

This document describes the JSON format for FLECC (Fixed Length Error Correcting Codes) problem instances.

## Schema

```json
{
  "nbCharacter": <integer>,
  "codeWordLength": <integer>,
  "numOfCodeWords": <integer>,
  "maxDist": <integer>,
  "minDist": <integer>,
  "dist": [[<integer>, ...], ...]
}
```

## Fields

- **nbCharacter**: Number of different characters/symbols available in the alphabet (e.g., 2 for binary, 3 for ternary)
- **codeWordLength**: Length of each codeword in the error correcting code
- **numOfCodeWords**: Number of codewords to be constructed for the error correcting code
- **maxDist**: Maximum allowed Hamming distance between any two codewords (upper bound constraint)
- **minDist**: Minimum required Hamming distance between any two codewords (error correction capability)
- **dist**: 2D distance matrix (nbCharacter × nbCharacter) specifying the distance/cost between each pair of characters

## Problem Description

The Fixed Length Error Correcting Codes (FLECC) problem involves constructing a set of codewords of fixed length that can detect and correct transmission errors. The key objectives are:

1. **Error Detection/Correction**: Ensure sufficient Hamming distance between codewords to detect/correct errors
2. **Efficiency**: Maximize the number of valid codewords while respecting distance constraints
3. **Alphabet Constraints**: Use only the available character set defined by nbCharacter

## Distance Constraints

- **Minimum Distance (minDist)**: Any two distinct codewords must have Hamming distance ≥ minDist
- **Maximum Distance (maxDist)**: Any two codewords must have Hamming distance ≤ maxDist
- **Distance Matrix**: The `dist` matrix defines custom distances between character pairs (beyond simple Hamming distance)

## Error Correction Capability

The minimum distance determines error correction capability:
- Distance d can detect up to (d-1) errors
- Distance d can correct up to ⌊(d-1)/2⌋ errors

## Example

```json
{
  "nbCharacter": 3,
  "codeWordLength": 5,
  "numOfCodeWords": 4,
  "maxDist": 8,
  "minDist": 3,
  "dist": [
    [0, 1, 2],
    [1, 0, 1], 
    [2, 1, 0]
  ]
}
```

This represents:
- Ternary alphabet (3 characters: 0, 1, 2)
- Codewords of length 5 
- Construct 4 different codewords
- Minimum Hamming distance of 3 between any two codewords (can correct 1 error)
- Maximum Hamming distance of 8
- Custom distance matrix where characters 0↔2 have distance 2, others have distance 1

## Typical Applications

- **Data Storage**: Error correction in memory systems, hard drives
- **Communications**: Error correction in network protocols, satellite communications
- **Digital Systems**: Reliable data transmission in noisy environments
- **Cryptography**: Construction of codes with specific distance properties

## Constraint Model 

```
% Fixed Length Error Correcting Codes - parametric MiniZinc model (derived from Essence prob036)
int: nbCharacter;
int: codeWordLength;  
int: numOfCodeWords;
int: maxDist;
int: minDist;

set of int: Character = 1..nbCharacter;
set of int: Index = 1..codeWordLength;

% Distance matrix between characters (symmetric)
array[Character, Character] of 0..maxDist: dist;

% Decision variables: set of codewords
% Each codeword is a sequence of characters
array[1..numOfCodeWords, Index] of var Character: codeword;

% Constraint: distance between any two distinct codewords >= minDist
constraint
    forall(i, j in 1..numOfCodeWords where i < j) (
        sum(k in Index) (dist[codeword[i,k], codeword[j,k]]) >= minDist
    );

% Symmetry breaking: order codewords lexicographically
constraint 
    forall(i in 1..numOfCodeWords-1) (
        lex_less(array1d(Index, [codeword[i,k] | k in Index]),
                 array1d(Index, [codeword[i+1,k] | k in Index]))
    );

solve satisfy;

output [
    "codeword = array2d(1..", show(numOfCodeWords), ", 1..", show(codeWordLength), ", ",
    show([codeword[i,j] | i in 1..numOfCodeWords, j in Index]), ");\n"
];

```

## Background

Understanding the structural characteristics of constraint programming instances is crucial for optimal solver performance. Different instance characteristics (graph density, problem size, connectivity patterns, etc.) influence which solver parameters work best.

The MiniZinc model provided above formalizes the problem. Your task is to analyze the instance structure and extract relevant characteristics that will be used by a separate system to configure solver parameters.

## Tasks

**MANDATORY FIRST STEP**: Begin your script with EXACTLY these imports (copy-paste them):

```python
# Import helper functions for input/output
from lmtune_helpers import input_data, output_results

# Import standard libraries for data analysis
import networkx as nx
import numpy as np
```

**WARNING**: 
- If you don't include the line `from lmtune_helpers import input_data, output_results`, your script will fail with NameError!
- DO NOT wrap this import in a try/except block!
- DO NOT check if the helpers are available!
- These functions WILL be available when your script runs!

Your script should:

1. Get the instance data using the `input_data()` function:
   ```python
   # Get the instance data using the helper function
   instance_data = input_data()
   ```
   
   **Important**: Your script should NOT perform any file I/O operations. All data input and output is handled by the framework through the imported helper functions.
   
   At the end of your script, you must use the `output_results()` function to return your findings:
   ```python
   # Return the results using the helper function
   output_results(result_dict)
   ```
   
   Where `result_dict` is a dictionary containing your instance characteristics analysis.

2. Analyze the instance structure and extract relevant characteristics. Use appropriate libraries like NetworkX for graph problems:
   
   Consider extracting metrics such as:
   - **Problem size**: Number of variables, constraints, decision points
   - **Graph properties** (for graph-based problems): Size, density, degree distribution, clustering coefficient, centrality measures, connectivity patterns
   - **Data distribution**: Statistical properties of weights, costs, capacities, demands
   - **Structural complexity**: Symmetries, regularity patterns, sparsity
   - **Problem-specific features**: Domain-specific characteristics that may influence solver behavior
   
   **Important**: You should analyze the MiniZinc model to understand what characteristics are most relevant for this specific problem type. Different problems require different analysis approaches.

3. Extract and compute the most relevant characteristics based on the problem structure shown in the MiniZinc model.

4. **MANDATORY TESTING**: You MUST call the `execute_script()` tool to test your script. If it fails, you MUST fix the errors and test again.

5. Return the instance characteristics using the `output_results(result_dict)` function as shown above.

## Expected Output Format

Your results should be a dictionary containing the extracted instance characteristics. **The dictionary MUST contain these specific keys:**

1. **"README"** (MANDATORY FIRST KEY): A text of approximately 200 words describing:
   - How the instance was analyzed (e.g., how graphs were constructed, what metrics were computed)
   - The methodology used to extract the characteristics
   - What each parameter represents and why it's relevant for this problem type

2. **Exactly 50 instance characteristic parameters** that describe key properties of this specific instance.

**CRITICAL OUTPUT FORMAT REQUIREMENT**: You MUST use standardized characteristic names `characteristic_1` through `characteristic_50`. This format is required for compatibility with the parallel processing system.

Example format:
```python
{
    "README": "This instance was analyzed by constructing a graph where nodes represent... The analysis focused on extracting structural properties that characterize the problem difficulty. The graph_density parameter measures the ratio of edges to possible edges, indicating constraint tightness. The clustering_coefficient captures local structure... [approximately 200 words total]",
    
    "characteristic_1": 100,      # n_customers
    "characteristic_2": 10,       # n_vehicles  
    "characteristic_3": 200,      # vehicle_capacity
    "characteristic_4": 1850,     # total_demand
    "characteristic_5": 18.5,     # avg_demand
    "characteristic_6": 5.4,      # std_demand
    "characteristic_7": 5,        # min_demand
    "characteristic_8": 34,       # max_demand
    "characteristic_9": 0.72,     # demand_skewness
    "characteristic_10": 3.1,     # demand_kurtosis
    "characteristic_11": 125000.0, # bounding_box_area
    "characteristic_12": 1.4,     # bounding_box_aspect_ratio
    "characteristic_13": 42.3,    # avg_distance_to_depot
    "characteristic_14": 12.7,    # std_distance_to_depot
    "characteristic_15": 10.2,    # min_distance_to_depot
    "characteristic_16": 75.6,    # max_distance_to_depot
    "characteristic_17": 37.2,    # avg_pairwise_distance
    "characteristic_18": 9.3,     # std_pairwise_distance
    "characteristic_19": 3.1,     # min_pairwise_distance
    "characteristic_20": 83.5,    # max_pairwise_distance
    "characteristic_21": 0.925,   # demand_to_capacity_ratio
    "characteristic_22": 0.0925,  # avg_demand_to_capacity_ratio
    "characteristic_23": 0.17,    # max_demand_to_capacity_ratio
    "characteristic_24": 0.12,    # pct_customers_high_demand
    "characteristic_25": 0.1,     # pct_routes_overloaded_estimate
    "characteristic_26": 0.89,    # capacity_utilization_estimate
    "characteristic_27": 1230.0,  # estimated_total_distance
    "characteristic_28": 11,      # estimated_n_routes
    "characteristic_29": 9.1,     # avg_customers_per_route
    "characteristic_30": 111.8,   # avg_route_distance
    "characteristic_31": 15.2,    # route_distance_std
    "characteristic_32": 12.5,    # graph_avg_degree
    "characteristic_33": 0.75,    # graph_density
    "characteristic_34": 1,       # graph_connectivity
    "characteristic_35": 8,       # graph_diameter
    "characteristic_36": 3.6,     # graph_avg_shortest_path
    "characteristic_37": 0.42,    # graph_clustering_coefficient
    "characteristic_38": 1095.7,  # mst_total_length
    "characteristic_39": 10.1,    # mst_std_edge_length
    "characteristic_40": 0.62,    # depot_centrality
    "characteristic_41": 12.7,    # customer_x_std
    "characteristic_42": 14.6,    # customer_y_std
    "characteristic_43": 0.73,    # spatial_entropy
    "characteristic_44": 0.31,    # spatial_gini_index
    "characteristic_45": 315.6,   # kmeans_inertia
    "characteristic_46": 0.41,    # silhouette_score
    "characteristic_47": 6,       # n_clusters_kmeans
    "characteristic_48": 16.7,    # avg_cluster_size
    "characteristic_49": 4.1,     # cluster_size_std
    "characteristic_50": 45.3     # inter_cluster_distance
}
```

**MANDATORY REQUIREMENTS**:
- The first key MUST be "README" with a ~200 word description
- You MUST include exactly 50 instance characteristic parameters (meaningful numeric values that describe the instance)
- **CRITICAL**: The 50 characteristics MUST be named `characteristic_1`, `characteristic_2`, ..., `characteristic_50` (NOT descriptive names)
- These parameters should capture essential properties like size, density, distribution, complexity, or structure
- You can add comments after each characteristic to indicate what it represents (as shown in the example)

**Important**: The 50 parameters should be the most informative characteristics that would help understand the instance's structure and difficulty. Use the standardized `characteristic_N` naming format for compatibility with the parallel processing system. You can document what each characteristic represents using inline comments as shown in the example above.

## Implementation Requirements

- Analyze the MiniZinc model to understand the problem structure
- Extract characteristics that are relevant for constraint solver performance
- Use appropriate analysis techniques (NetworkX for graphs, NumPy for statistics, etc.)
- Focus on structural and statistical properties that influence solving difficulty
- **MANDATORY**: Call `execute_script()` to test your implementation with the example data
- Ensure your results are well-structured with meaningful characteristics

Remember to use the imported `input_data()` and `output_results()` functions. Your script should not perform any file I/O operations - all data access is handled through these helper functions.

## Complete Example Script

Here's a complete example showing the REQUIRED structure:

```python
# Import helper functions for input/output
# IMPORTANT: You MUST import these functions at the beginning of your script
# DO NOT define your own versions of these functions!
from lmtune_helpers import input_data, output_results

# Import standard libraries for data analysis
import networkx as nx
import numpy as np

# DO NOT create a get_instance_data() function!
# DO NOT import inside functions!
# DO NOT use try/except for imports!

def main():
    """Extract instance characteristics from the problem data."""
    
    # Get the instance data using the helper function
    instance_data = input_data()
    
    # Initialize results dictionary with required structure
    results = {
        "README": "This instance was analyzed by... [your ~200 word description]",
        "characteristic_1": 0.0,
        "characteristic_2": 0.0,
        "characteristic_3": 0.0,
        "characteristic_4": 0.0,
        "characteristic_5": 0.0,
        "characteristic_6": 0.0,
        "characteristic_7": 0.0,
        "characteristic_8": 0.0,
        "characteristic_9": 0.0,
        "characteristic_10": 0.0,
        "characteristic_11": 0.0,
        "characteristic_12": 0.0,
        "characteristic_13": 0.0,
        "characteristic_14": 0.0,
        "characteristic_15": 0.0,
        "characteristic_16": 0.0,
        "characteristic_17": 0.0,
        "characteristic_18": 0.0,
        "characteristic_19": 0.0,
        "characteristic_20": 0.0,
        "characteristic_21": 0.0,
        "characteristic_22": 0.0,
        "characteristic_23": 0.0,
        "characteristic_24": 0.0,
        "characteristic_25": 0.0,
        "characteristic_26": 0.0,
        "characteristic_27": 0.0,
        "characteristic_28": 0.0,
        "characteristic_29": 0.0,
        "characteristic_30": 0.0,
        "characteristic_31": 0.0,
        "characteristic_32": 0.0,
        "characteristic_33": 0.0,
        "characteristic_34": 0.0,
        "characteristic_35": 0.0,
        "characteristic_36": 0.0,
        "characteristic_37": 0.0,
        "characteristic_38": 0.0,
        "characteristic_39": 0.0,
        "characteristic_40": 0.0,
        "characteristic_41": 0.0,
        "characteristic_42": 0.0,
        "characteristic_43": 0.0,
        "characteristic_44": 0.0,
        "characteristic_45": 0.0,
        "characteristic_46": 0.0,
        "characteristic_47": 0.0,
        "characteristic_48": 0.0,
        "characteristic_49": 0.0,
        "characteristic_50": 0.0
    }
    
    try:
        # Extract basic problem parameters
        # (Adapt based on the actual fields in your problem's JSON schema)
        n = instance_data.get('N', 0)
        capacity = instance_data.get('Capacity', 0)
        
        # Analyze the instance and compute your 5 characteristic parameters
        # ... your analysis code here ...
        
        # Update the results with computed values
        results["README"] = "Your detailed ~200 word description..."
        results["characteristic_1"] = computed_value_1
        # ... etc ...
        
    except Exception as e:
        # Handle errors gracefully
        results["error"] = str(e)
    
    # Return the results using the helper function
    output_results(results)

if __name__ == "__main__":
    main()
```

**CRITICAL**: 
- You MUST import `from lmtune_helpers import input_data, output_results`
- You MUST use `input_data()` to get the instance data (not hard-code it)
- You MUST use `output_results(results)` to output the results dictionary
- You MUST use standardized names `characteristic_1` through `characteristic_50` in your results dictionary
- You MUST test your script with `execute_script()` and fix any errors - no exceptions!