Combinatorial optimization problems are fundamental challenges in modern computing, particularly in logistics, network design, and operations research. The Facility Location Problem (FLP) and Traveling Salesman Problem (TSP) represent prime examples of these challenges. In this post, we’ll explore the development of a sophisticated optimizer framework using Micronaut, featuring real-time progress tracking and a modular, extensible architecture.

With the exciting announcement of the free plan for Github Copilot, we’ll leverage AI-assisted development to create our Micronaut Optimizer framework. This powerful combination of tools will help us tackle complex combinatorial optimization problems efficiently.

Background and Motivation

Building on our previous exploration of Optimizing TSP with Genetic Algorithms, this post delves deeper into creating a versatile optimization framework. Our focus will be on the architectural decisions and implementation details that make this framework both powerful and extensible.


Architecture Overview

The architecture of our Micronaut Optimizer framework is designed with modularity and scalability in mind. Below is a high-level architectural diagram that illustrates the key components and their interactions:

optimizer-architecture


Core Components

The framework’s architecture is built around several key components, each serving a specific purpose while maintaining loose coupling for maximum flexibility:

Controller Layer

The controller layer serves as the entry point for HTTP requests, implementing a clean REST API interface. Here’s an example of our FLPController:

// filepath: src/main/java/io/github/seehiong/controller/FLPController.java
package io.github.seehiong.controller;

@Controller("/flp")
@RequiredArgsConstructor
public class FLPController {
    private final FLPService flpService;

    @Post(value = "/uploadSolve", produces = MediaType.TEXT_EVENT_STREAM, consumes = MediaType.MULTIPART_FORM_DATA)
    public Flux<Object> uploadSolve(@Body FLPInput input, CompletedFileUpload file) throws IOException {
        return flpService.uploadSolve(input, file);
    }
}

Service Layer

The service layer encapsulates our business logic and orchestrates the optimization process. The FLPService demonstrates this orchestration:

// filepath: src/main/java/io/github/seehiong/service/FLPService.java
package io.github.seehiong.service;

@Singleton
@RequiredArgsConstructor
public class FLPService {
     public Flux<Object> uploadSolve(FLPInput input, CompletedFileUpload file) {
        Solver<FLPInput, FLPOutput> solver = (Solver<FLPInput, FLPOutput>) solverFactory.getSolver(input.getProblemType());
        return solver.solve(input, (PublishSubject<FLPOutput>) progressSubject);
    }
}

Factory Pattern Implementation

Our SolverFactory implements a clean factory pattern to instantiate appropriate solvers based on the problem type:

// filepath: src/main/java/io/github/seehiong/factory/SolverFactory.java
package io.github.seehiong.factory;

@Singleton
public class SolverFactory {
    public Solver<?, ?> getSolver(ProblemType problemType) {
        return switch (problemType) {
            case TSP -> new TSPSolver();
            case TSP_GA -> new TSPGaSolver();
            case FLP -> new FLPSolver();
            default -> throw new IllegalArgumentException("Unknown problem type: " + problemType);
        };
    }
}

Solver Interface

The Solver interface defines the contract that all optimization implementations must fulfill:

// filepath: src/main/java/io/github/seehiong/solver/Solver.java
package io.github.seehiong.solver;

public interface Solver<I extends Input, O extends Output> {
    Flux<Object> solve(I input, PublishSubject<O> progressSubject);
}

Reactive Programming Integration

Our framework leverages reactive programming through Project Reactor’s Flux and RxJava’s PublishSubject. This enables:

  • Real-time progress updates to clients
  • Non-blocking execution of optimization algorithms
  • Efficient handling of long-running computations

The FLPSolver implementation showcases this reactive approach:

// filepath: src/main/java/io/github/seehiong/solver/FLPSolver.java
package io.github.seehiong.solver;

public class FLPSolver implements Solver<FLPInput, FLPOutput> {
    @Override
    public Flux<Object> solve(FLPInput input, PublishSubject<FLPOutput> progressSubject) {
        // Optimization logic with reactive progress updates
    }
}

Real-Time Visualization

A key feature of our framework is its ability to visualize optimization progress in real-time, providing valuable insights into the solver’s behavior.

Server-Sent Events Implementation

We implement Server-Sent Events (SSE) to stream optimization progress to clients:

// filepath: src/main/java/io/github/seehiong/controller/FLPController.java
package io.github.seehiong.controller;

public class FLPController {
    @Get(value = "/progress/{solverId}", produces = MediaType.TEXT_EVENT_STREAM)
    public Flowable<Event<FLPOutput>> streamProgress(String solverId) {
        return flpService.streamProgress(solverId);
    }
}

Interactive Visualization Interface

Our front-end implementation provides real-time visualization of the optimization process:

<!-- filepath: src/main/resources/static/flp-progress.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>FLP Optimization Progress</title>
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</head>
<body>
    <h1>FLP Optimization Progress</h1>
    <canvas id="flpChart" width="800" height="600"></canvas>

    <script>
        const eventSource = new EventSource(`/flp/progress/${solverId}`);
        eventSource.onmessage = function (event) {
            const data = JSON.parse(event.data);
            // Visualization logic
        };
    </script>
</body>
</html>

Framework in Action

Let’s examine how our framework handles different optimization problems:

Facility Location Problem (FLP)

Consider this sample FLP input:

3 8
100 100 1065.0 1065.0
100 100 1062.0 1062.0
100 500 0.0 0.0
50 1397.0 1397.0
50 1398.0 1398.0
75 1399.0 1399.0
75 586.0 586.0
80 900.0 900.0
80 910.0 910.0
90 1200.0 1200.0
90 1210.0 1210.0

The visualization of the solved FLP problem:

optimizer-flp-upload-solved

Traveling Salesman Problem (TSP)

For the TSP implementation, we can process complex distance matrices:

{
    "problemType": "TSP",
    "distanceMatrixConstraint": {
        "distanceMatrix": [
            [0, 10, 15, 20, 25, 30, 35, 40],
            [10, 0, 35, 25, 20, 45, 50, 55],
            [15, 35, 0, 30, 10, 40, 45, 60],
            [20, 25, 30, 0, 15, 50, 55, 70],
            [25, 20, 10, 15, 0, 35, 40, 65],
            [30, 45, 40, 50, 35, 0, 25, 70],
            [35, 50, 45, 55, 40, 25, 0, 75],
            [40, 55, 60, 70, 65, 70, 75, 0]
        ]
    }
}

The optimization result:

optimizer-tsp-solved

Genetic Algorithm TSP Implementation

For larger problems, our genetic algorithm implementation shows impressive results:

The final optimization result:

optimizer-tsp-ga-solved


Future Directions and Conclusion

The Micronaut Optimizer Framework represents a powerful, flexible solution for tackling complex optimization problems. Its reactive architecture and real-time visualization capabilities make it an excellent choice for both research and production environments.

Planned Enhancements

  1. Additional Solver Integration

    • Support for Vehicle Routing Problems (VRP)
    • Implementation of advanced scheduling algorithms
    • Integration with quantum computing solvers
  2. Performance Optimizations

    • Distributed computing support
    • GPU acceleration for applicable algorithms
    • Advanced caching strategies
  3. Visualization Improvements

    • Interactive 3D visualizations
    • Advanced analytics dashboards
    • Real-time performance metrics
  4. Architecture Extensions

    • Implementation of CP-SAT callbacks
    • Enhanced error handling and recovery
    • Expanded API documentation

Get Involved

The complete implementation is available on GitHub: Micronaut-Optimizer. We welcome contributions and feedback from the community!