What is MDEO Cloud?
MDEO Cloud is a web-based platform for model-driven engineering optimisation: you describe a design space as a metamodel, a starting model and a set of model transformations, state what makes one design better than another, and let a search algorithm explore the space for you.
It is a fork of MDEOptimiser that replaces the Eclipse-based tooling with a browser workbench and a distributed execution platform.
The problem it solves
Many engineering problems are really search problems over structured artefacts:
- Which components should each class of a system own, so that cohesion is high and coupling is low?
- How should tasks be spread across a team without overloading anyone?
- Which set of features fits into the next release, given a budget and a dependency graph?
All of these have the same shape. There is a structure (a model), a set of legal edits to that structure, and one or more numeric criteria that say how good a structure is. MDEO Cloud lets you write down exactly those three things and hands the rest — mutation, evaluation, multi-objective search, parallel execution — to the platform.
What you write
A complete optimisation problem is five kinds of file, each with its own language, editor and validation:
| File | Language | Purpose |
|---|---|---|
.mm | Metamodel | The structure of your domain: classes, properties, associations |
.m | Model | A concrete instance to start the search from |
.mt | Model Transformation | The legal edits — the moves the search may make |
.fn | Script | Objectives and constraints, as ordinary functions over the model |
.config | Config | Which files to use, what to optimise, and how to search |
Here is what that looks like end to end. First the domain:
// Domain of the task allocation problem: a project owns tasks and developers,
// and every task may be assigned to at most one developer.
enum Priority {
LOW
MEDIUM
HIGH
}
class Project {
name: string
}
abstract class WorkItem {
name: string
effort: int
}
class Task extends WorkItem {
priority: Priority
}
class Developer {
name: string
capacity: int
}
Project.tasks[*] *--> Task.project
Project.developers[*] *--> Developer.project
Task.assignee[0..1] <--> Developer.tasks[*]A starting point:
using "./tasks.mm"
apollo : Project {
name = "Apollo"
}
login : Task {
name = "Login screen"
effort = 5
priority = Priority.HIGH
}
reporting : Task {
name = "Reporting dashboard"
effort = 8
priority = Priority.MEDIUM
}
migration : Task {
name = "Database migration"
effort = 13
priority = Priority.LOW
}
alice : Developer {
name = "Alice"
capacity = 10
}
bob : Developer {
name = "Bob"
capacity = 15
}
apollo.tasks -- login
apollo.tasks -- reporting
apollo.tasks -- migration
apollo.developers -- alice
apollo.developers -- bobThe moves the search is allowed to make:
using "./tasks.mm"
// Give an unassigned task to some developer.
match {
task: Task {
effort > 0
}
developer: Developer { }
create task.assignee -- developer
}What "better" means:
using "./tasks.mm"
// Total effort of every task that nobody has picked up yet.
fun unassignedEffort(): int {
var total = 0
for (task in Task.all()) {
if (task.assignee == null) {
total = total + task.effort
}
}
return total
}
// How much the busiest developer is overloaded beyond their capacity.
fun maxOverload(): int {
var worst = 0
for (developer in Developer.all()) {
var assigned = 0
for (task in developer.tasks) {
assigned = assigned + task.effort
}
var overload = assigned - developer.capacity
if (overload > worst) {
worst = overload
}
}
return worst
}
// Constraint: every high priority task has to be assigned.
// 0 means satisfied, any larger value is the magnitude of the violation.
fun unassignedHighPriority(): int {
var violations = 0
for (task in Task.all()) {
if (task.priority == Priority.HIGH && task.assignee == null) {
violations = violations + 1
}
}
return violations
}And finally, how to run it:
problem {
metamodel = "./tasks.mm"
model = "./plan.m"
}
goal {
import { unassignedEffort, maxOverload, unassignedHighPriority } from "./objectives.fn"
minimize unassignedEffort
minimize maxOverload
constraint unassignedHighPriority
}
search {
mutations {
using "./assign.mt"
using "./unassign.mt"
}
}
solver {
algorithm = NSGAII
parameters {
population = 40
variation = mutation
mutation {
step = 1
strategy = random
}
}
termination {
evolutions = 500
}
}
runtime {
timeout {
script = 1000
transformation = 1000
}
resources {
threads = 4
}
}The walkthrough builds this example up step by step and runs it.
What makes it different
Everything runs in the browser. Metamodels and models have graphical editors as well as textual ones, and both views edit the same file. Language support — completion, validation, formatting, go-to-definition — comes from Langium language servers that run in a web worker, so editing stays responsive without a round trip to a server.
Search runs somewhere else. An optimisation run is dispatched to dedicated execution nodes. A single run can be spread across several nodes and many threads, and the workbench streams progress back over a WebSocket while you keep working.
Every language is a plugin. The five languages above are not built into the workbench. Each one is served by a plugin — an HTTP service that publishes a manifest describing the languages, editors and language extensions it provides. The workbench discovers plugins at runtime and loads their code as ES modules. Adding a sixth language means writing a plugin, not patching the workbench.
That last point is the reason this platform exists as a platform: see the plugin catalogue for what ships with it, and the developer guide for how to add your own.
Where to go next
- Core concepts — the vocabulary used throughout these docs
- Architecture — how the pieces fit together
- Getting started — run MDEO Cloud locally
- Walkthrough — build and run a complete optimisation