Model API
The Model is the central component of JuLS that orchestrates the optimization process.
Model Creation
JuLS.init_model
— Functioninit_model(
e::Experiment;
init::InitializationHeuristic = default_init(e),
neigh::NeighbourhoodHeuristic = default_neigh(e),
pick::MoveSelectionHeuristic = default_pick(e),
using_cp::Bool = default_using_cp(e)
)
Initializes an optimization model for the given experiment.
Process
- Generates variable domains
- Creates initial solution
- Generates decision variables
- Creates and initializes DAG
- Configures model with specified heuristics and CP settings
Optimization
JuLS.optimize!
— Functionoptimize!(model::AbstractModel;
rng = Random.GLOBAL_RNG,
n_iterations::Int = 100)
Main optimization function that iteratively improves the solution.
Arguments
model::AbstractModel
: Model to optimizerng
: Random number generatorn_iterations::Int
: Number of iterations to perform
Limits
IterationLimit(n)
: Stop after n iterationsTimeLimit(seconds)
: Stop after specified time in seconds
Model Structure
The Model
struct contains the following key fields:
experiment
: The problem definitioncurrent_solution
: Current solution statebest_solution
: Best solution found so fardag
: The constraint/objective DAGheuristics
: Collection of heuristics usedmetrics
: Performance metrics and statistics
Solution Access
Current Solution
Access the current solution state:
model.current_solution.values # Current variable values
model.current_solution.objective # Current objective value
model.current_solution.feasible # Feasibility status
Best Solution
Access the best solution found:
model.best_solution.values # Best variable values
model.best_solution.objective # Best objective value
Metrics and Statistics
The model tracks various metrics during optimization:
model.run_metrics
Example Usage
using JuLS
# Create experiment
data_path = joinpath(JuLS.PROJECT_ROOT, "data", "knapsack", "ks_4_0")
experiment = KnapsackExperiment(data_path)
# Initialize model
model = init_model(
experiment;
init = SimpleInitialization(),
neigh = BinaryRandomNeighbourhood(10, 2), # 10 moves, 2 variables per move
pick = GreedyMoveSelection(),
using_cp = true
)
# Optimize
optimize!(model; limit = IterationLimit(1000))
# Access results
println("Best objective: ", model.best_solution.objective)
println("Best solution: ", model.best_solution.values)
Advanced Usage
Custom Stopping Criteria
You can implement custom stopping criteria by extending the Limit
types.
Model Inspection
During optimization, you can inspect the model state:
# Check if current solution is feasible
is_feasible = model.current_solution.feasible
# Get constraint violations
violations = get_violations(model.dag)
# Access move history
recent_moves = model.move_history[end-10:end]
Performance Considerations
Memory Usage
- The model stores solution history for analysis
- Consider smaller neighbourhood sizes for large problems
Threading
- JuLS can utilize multiple threads for move evaluation
- Start Julia with
--threads=auto
for best performance - Some heuristics are inherently sequential
Constraint Programming Integration
When using_cp = true
:
- Move filtering is more efficient but requires more memory
- CP solver state is maintained alongside the DAG
- Infeasible moves are filtered before evaluation
Troubleshooting
Common Issues
- No improvements found: Try different heuristic combinations
- Memory issues: Reduce neighbourhood size or clear history
- Slow convergence: Enable CP filtering or use better initialization
- Infeasible solutions: Check constraint definitions in DAG