Move Types Reference
Move types define how Local Search explores the solution space. Each move type searches a different "neighborhood" of solutions by trying different types of object reassignments.
Overview
When using the Local Search solver, you specify which move types to use. The solver tries each move type in order, looking for improvements to the objective function.
Key Concepts:
- Hot container: The container contributing most to the objective (highest cost/constraint violation)
- Move evaluation: Testing how a potential move affects the objective
- Neighborhood: The set of solutions reachable by a specific move type
Quick Reference
| Move Type | Category | Complexity | Use When |
|---|---|---|---|
| Single | Basic | O(objects × containers) | Always - fundamental move type |
| SingleFast | Basic | O(objects × containers) | Need faster convergence, can accept local optimum |
| SingleGreedy | Basic | O(objects × containers) | Speed critical, single-threaded OK |
| Swap | Swap | O(objects²) | Capacity constrained problems |
| SwapFullContainers | Swap | O(containers²) | Moving full containers together |
| SwapFullWithEmpty | Swap | O(objects × containers) | Consolidating to free containers |
| SwapSampled | Swap | O(sample²) | Large problems, need sampling |
| SingleChain | Chain | O(objects² × containers) | Capacity constrained, need 2-move chains |
| SingleChainFast | Chain | O(objects² × containers) | Chain moves with early termination |
| SingleEndChain | Chain | O(objects² × containers) | Better than SingleChain (recommended) |
| TripleLoop | Advanced | O(objects³) | Need to escape deep local optima |
| KLSearch | Advanced | Expensive | Graph partitioning problems |
| ColocateGroups | Group | Varies | Need to move related objects together |
| GroupRouting | Group | Varies | Routing with failover groups |
| FixedDest | Fixed | O(objects) | Filling specific containers |
| FixedSource | Fixed | O(objects × containers) | Draining specific containers (ToFree) |
Categories
Basic Moves
Single-object moves that form the foundation of most local search strategies:
- Single: Move one object to any container (most fundamental)
- SingleFast: Single moves with early termination
- SingleGreedy: Greedy single moves (single-threaded)
- SingleRandomBatches: Parallel batched single moves
- SingleRandomStratified: Stratified sampling of destinations
- SingleColdestStratified: Target coldest containers per stratum
- SingleRandomObjectStratified: Sample objects, fixed dest
Swap Moves
Exchange objects between containers (useful when capacity-constrained):
- Swap: Exchange any two objects
- SwapFullContainers: Exchange all objects between two containers
- SwapFullWithEmpty: Move all objects to empty container
- SwapSampled: Sample subset of swap moves
Chain Moves
Multi-move sequences where objects move in a chain:
- SingleChain: 2-move chain (hot container in middle)
- SingleChainFast: Fast 2-move chain with parallelization
- SingleEndChain: 2-move chain (hot container at end) - recommended
Group Moves
Move multiple related objects together:
- ColocateGroups: Move related groups to same scope item
- GroupRouting: Routing-aware group moves
- GroupMoveWithHintStrategies: Group moves with strategies
- GreedyGroupToScopeItem: Greedy group placement
Fixed Source/Destination
Moves with predetermined source or destination containers:
- FixedDest: Move objects to specific container(s)
- FixedSource: Move objects from specific container(s)
- FixedDestMultiMove: Multi-object bundles to fixed dest
- FixedSourceMultiMove: Multi-object bundles from fixed source
- FixedDestSwapMultiMove: Swap bundles with fixed dest
Advanced Moves
Complex move types for escaping local optima:
- TripleLoop: 3-object cyclic moves
- KLSearch: Kernighan-Lin inspired search
- ReplicaDrop: Drop replica from specific scope
Choosing Move Types
Default Configuration
For most problems, start with:
solver_spec = {
"localSearchSolverSpec": {
"moveTypeList": [
{"singleMoveTypeSpec": {}}, # Basic single-object moves
{"swapMoveTypeSpec": {}}, # Add swap for capacity-constrained problems
]
}
}
solver.add_solver(solver_spec)
By Problem Type
Unconstrained or soft-constrained:
- Start with
Singleonly - Add
SingleFastif convergence too slow
Capacity-constrained:
- Use
Single+Swap - Add
SingleEndChainfor better quality - Consider
SwapSampledfor large problems
Group placement:
- Use
ColocateGroupswhen groups must be together - Use
GroupRoutingfor routing-aware placement
Container draining (ToFree):
- Use
FixedSourcefor draining specific containers - Much faster than exploring all source containers
Stuck in local optimum:
- Add
TripleLoopfor deeper search - Try
KLSearchfor graph partitioning problems - Use multiple runs with different seeds
Performance Trade-offs
| Priority | Move Types | Notes |
|---|---|---|
| Speed | Single or SingleFast only | Fast but may miss better solutions |
| Quality | Single + Swap + SingleEndChain | Balanced speed/quality |
| Best Solution | All applicable move types | Slow but thorough |
Move Type Order Matters
Move types are tried in the order specified. The solver:
- Tries moves from hottest container using first move type
- If improvement found → applies move, restart from step 1
- If no improvement → try second move type on same container
- If still no improvement → move to second hottest container, restart from step 1
- Continue until no improving move found from any container with any move type
Recommendation: Order move types from fast to slow:
move_type_list = [
{"singleFastMoveTypeSpec": {}}, # Try fast moves first
{"singleMoveTypeSpec": {}}, # Then thorough single moves
{"swapMoveTypeSpec": {}}, # Then swaps
{"singleEndChainMoveTypeSpec": {}}, # Finally, expensive chain moves
]
Common Patterns
Fast Interactive Solving
Quick responses for UI/dashboards:
# Use fast move types only
move_type_list = [{"singleFastMoveTypeSpec": {}}]
Production Rebalancing
Balance speed and quality:
# Good default for most production use cases
move_type_list = [
{"singleMoveTypeSpec": {}},
{"swapMoveTypeSpec": {}},
]
Offline Optimization
Take time to find best solution:
# Use all applicable move types
move_type_list = [
{"singleMoveTypeSpec": {}},
{"swapMoveTypeSpec": {}},
{"singleEndChainMoveTypeSpec": {}},
{"tripleLoopMoveTypeSpec": {}},
]
Draining Containers
When using ToFreeSpec:
# Much faster than exploring all containers
move_type_list = [
{
"singleFixedSourceMoveTypeSpec": {
"scopeItemList": {
"scopeName": "container",
"scopeItems": ["host1", "host2"], # Containers to drain
}
}
}
]
Configuring Move Types
Each move type has its own configuration options. See individual move type pages for details.
Example: Configuring Swap with Sampling
from rebalancer.specs import MoveTypeSpec, SampleSize, SwapMoveTypeSpec
swap_spec = MoveTypeSpec(
swapMoveTypeSpec=SwapMoveTypeSpec(
greedyOnSrc=True, # Exit early on source objects
sampleSize=SampleSize(
defaultSampleSize=100, # Sample 100 objects
),
)
)
Example: Configuring Fixed Destination
from rebalancer.specs import FixedDestMoveTypeSpec, MoveTypeSpec, SampleSize
fixed_dest_spec = MoveTypeSpec(
fixedDestMoveTypeSpec=FixedDestMoveTypeSpec(
specialContainer="spare_capacity_container",
sampleSize=SampleSize(defaultSampleSize=50),
)
)
Complexity Analysis
Understanding complexity helps choose appropriate move types for your problem size:
| Move Type | Per-Container Complexity | Problem Size Limit |
|---|---|---|
| Single | O(N × C) | 100K+ objects |
| SingleFast | O(N × C) with early exit | 100K+ objects |
| Swap | O(N²) | 10K objects |
| SingleChain | O(N² × C) | 5K objects |
| TripleLoop | O(N³) | 1K objects |
Where N = objects per container, C = number of containers
Tip: For large problems (>10K objects), prefer:
Single,SingleFast, orSingleRandomStratified(sampled)SwapSampledinstead ofSwap- Avoid
TripleLoopandKLSearch
Troubleshooting
Problem: Solver too slow
Diagnosis: Too many objects or expensive move types
Solutions:
- Use faster move types (
SingleFast,SingleGreedy) - Add sampling to expensive types (
SwapSampled) - Reduce time limits per move type
- Use stratified sampling moves
Problem: Stuck in local optimum
Diagnosis: Terminates quickly with NO_IMPROVING_MOVE but poor objective
Solutions:
- Add more powerful move types (
SingleEndChain,TripleLoop) - Use better initial assignment
- Try multiple runs with different random seeds
- Check if problem is feasible
Problem: Not improving constraints
Diagnosis: Solution violates constraints
Solutions:
- Verify constraints are achievable
- Use move types that can escape constraint violations (Swap, Chain moves)
- Check if initial assignment is extremely broken
- Increase constraint penalties (
invalidCost)
Next Steps
- Choose a move type from the categories above to see detailed documentation
- Review Local Search Solver to understand how move types fit into the solver
- See Solver Strategies for multi-stage solving with different move types
- Check Performance Guide for tuning move type performance
Source Code
- Thrift definitions:
interface/thrift/SolverSpecs.thrift - Move type implementations:
solver/moves/ - Tests:
solver/moves/tests/