Fraunhofer Institute IAIS, Sankt Augustin · City St George’s, University of London · Lamarr Institute for ML and AI
SoftSky is a tolerant approach to spatial multi-criteria decision analysis. It relaxes the Pareto frontier with a per-criterion tolerance — a soft sky — so that near-optimal options are admitted rather than discarded, and then groups the survivors into a few interpretable, named types — constellations. This page accompanies a Visualization Viewpoints article on how the method went from a long-held idea to a running prototype in an afternoon, using a workflow language (ATWL) as a design scaffold and an AI assistant as a build engine.
The complete workflow — Context-Aware Tolerant-Pareto decision analysis (CATP) — is a graph of typed artifacts joined by transforms across seven stages, with refinement loops and feedback paths. Novelty-bearing steps are marked [NOVEL].
The workflow supports staged spatial multi-criteria decision analysis over a large set of imperfect real-world options. The analyst first focuses the option space spatially (geo centre + distance threshold), then selects numeric criteria with redundancy awareness and defines utility functions over visualized value distributions. Two extensions of traditional MCDA follow. (1) Decision contexts: machine-proposed groupings (NMF / archetypal analysis) are translated into human-readable predicates, which the analyst edits into named, persistent, rule-based decision contexts (e.g., "studios", "family flats"); dominance is computed only within contexts, eliminating semantically meaningless cross-type domination. (2) Tolerant Pareto optimality: per-criterion tolerance thresholds are derived from measured data imperfection (value variation among cross-wave duplicate listings); the binary frontier is replaced by a graded robustness margin per option (the smallest tolerance change that flips its frontier membership), with optimistic/pessimistic bounding for records with missing values. Coordinated map / ranking / stability views support interactive sensitivity analysis over tolerances, utilities, and context definitions. The outcome is a per-context shortlist of robust options with explicit sensitivity statements and a cross-context comparison.
workflow context-tolerant-pareto template: characterise (distance) → visualise → generate-knowledge (spatial focus) → define-unit (spatial selection) → characterise (correlations) → visualise (distributions) → generate-knowledge (criteria choice) → loop(generate-knowledge (utility functions) → characterise (utilities) → visualise → assess) → characterise (noise estimation) → generate-knowledge (tolerances) → loop(model (group proposal) → abstract (rule description) → generate-knowledge (decision contexts) → define-unit (partition) → visualise → assess) → loop(characterise (tolerant dominance, robustness) → contextualise (map) → visualise → assess → generate-knowledge (adjust specifications)) → abstract (trade-off interpretation) → generate-knowledge (decision) description: "Staged spatial MCDA over imperfect data: spatial focusing, criteria and utility elicitation, predicate-based decision contexts bridging machine grouping and human definition, and graded tolerant-Pareto ranking with data-grounded tolerances and interactive sensitivity analysis" # ============================================================================ # INPUT: Rental offers and analyst focus # ============================================================================ artifact D_offers : entities origin: given internal structure: elementary embedment: {set, space} features: - id: f_location value structure: atomic value type: spatial description: "Geographic position of the offered apartment" - id: f_numeric_attrs value structure: vector value type: numeric description: "Numeric attributes: base rent, total rent, service charge, living space, number of rooms, floor, year constructed, picture count, etc." - id: f_scrape_wave value structure: atomic value type: temporal description: "Scraping wave identifier (Sep 2018 / May 2019 / Oct 2019); enables duplicate-based noise estimation" description: "Apartment rental offers scraped from a real-estate platform; imperfect: outliers, missing values, cross-wave duplicates" artifact S_center : specification origin: given representation form: "parameter settings" description: "Analyst-selected geographic centre of interest (point)" # ============================================================================ # STAGE 1: SPATIAL FOCUSING # ============================================================================ transform T_distance : intent: characterise manner: "compute spatial distance to centre" input: D_offers, S_center output: F_distance actor: machine description: "Compute distance of every offer to the selected centre" artifact F_distance : feature(D_offers) value structure: atomic value type: numeric description: "Distance (km) from offer location to centre of interest" transform T_vis_reach : intent: visualise manner: "cumulative distribution plot" input: F_distance output: V_reach actor: machine description: "Show number of available offers as a function of distance threshold to support choice of spatial focus" artifact V_reach : visualisation(F_distance) layout: "distance axis" form: "cumulative count curve" encoding: "x: distance threshold; y: number of offers within threshold" description: "Reach diagram: option supply vs. spatial extent" transform T_fix_focus : intent: generate-knowledge manner: "select distance threshold" input: V_reach output: S_focus actor: human description: "Analyst fixes the maximal acceptable distance, balancing option supply against spatial relevance" artifact S_focus : specification representation form: "parameter settings" description: "Maximal distance threshold (e.g., 50 km) defining the spatial scope of analysis" transform T_select : intent: define-unit manner: "select by spatial distance threshold" input: D_offers, F_distance, S_focus output: D_cand actor: machine description: "Select candidate options within the spatial focus" artifact D_cand : entities internal structure: elementary embedment: {set, space} features: - id: f_numeric_attrs value structure: vector value type: numeric description: "Numeric attributes inherited from D_offers" description: "Candidate decision options within the spatial focus" # ============================================================================ # STAGE 2: CRITERIA SELECTION (redundancy-aware) # ============================================================================ transform T_correlate : intent: characterise manner: "pairwise rank correlation of numeric attributes" input: D_cand output: F_corr actor: machine description: "Quantify redundancy among candidate criteria (e.g., base rent vs. total rent, space vs. rooms)" artifact F_corr : feature(D_cand) value structure: matrix value type: numeric representation form: "pairwise correlation matrix" description: "Attribute redundancy structure" transform T_vis_attrs : intent: visualise manner: "histograms with correlation overview" input: D_cand, F_corr output: V_attrs actor: machine description: "Show value distributions of all numeric attributes (percentile-clipped against outliers) and their correlations" artifact V_attrs : visualisation(D_cand, F_corr) layout: "small multiples + matrix" form: "histograms; coloured matrix cells" encoding: "histogram per attribute; cell colour: correlation strength" description: "Attribute overview supporting informed, non-redundant criteria selection" transform T_choose_criteria : intent: generate-knowledge manner: "select criteria and optimization directions" input: V_attrs output: S_criteria actor: human description: "Analyst selects the numeric criteria of interest and their directions (minimise/maximise), warned about redundant pairs" artifact S_criteria : specification representation form: "criteria" description: "Selected criteria subset with optimisation directions; distance (F_distance) is always included as a criterion" # ============================================================================ # STAGE 3: UTILITY ELICITATION OVER DISTRIBUTIONS # ============================================================================ loop L_utility: purpose: "Define and refine per-criterion utility functions against the background of empirical value distributions" until: "Utility functions adequately express analyst preferences" body: transform T_define_util : intent: generate-knowledge manner: "interactive specification of piecewise-linear value functions" input: V_attrs, S_criteria output: S_util actor: human description: "Analyst draws/edits utility functions (possibly non-monotonic, e.g., ideal floor) over distribution histograms shown as background" artifact S_util : specification representation form: "parameter settings" description: "Piecewise-linear utility function per criterion" transform T_apply_util : intent: characterise manner: "apply utility functions" input: D_cand, S_util, S_criteria output: F_util actor: machine description: "Compute per-criterion utility values for all candidates" artifact F_util : feature(D_cand) value structure: vector value type: numeric description: "Utility value per selected criterion per option" transform T_vis_util : intent: visualise manner: "utility curves over histograms" input: V_attrs, S_util, F_util output: V_util actor: machine description: "Overlay utility functions and resulting utility distributions on attribute histograms" artifact V_util : visualisation(S_util, F_util) layout: "small multiples per criterion" form: "curves over histograms" encoding: "x: attribute value; bars: frequency; curve: utility" description: "Visual feedback on utility specifications" transform T_assess_util : intent: assess manner: "judge adequacy of utility functions" input: V_util output: util_assessment actor: human description: "Analyst checks whether utilities reflect preferences; decides on refinement" artifact util_assessment : knowledge(S_util) representation form: "quality judgment" description: "Adequacy judgment of current utility functions" if util_assessment indicates refinement needed: then: continue loop L_utility else: exit loop L_utility end loop L_utility # ============================================================================ # STAGE 4: DATA-GROUNDED TOLERANCE DERIVATION # [NOVEL] # ============================================================================ transform T_noise : intent: characterise manner: "estimate per-criterion value uncertainty from cross-wave duplicate listings and value reporting granularity" input: D_offers output: F_noise actor: machine description: "Measure empirical value variation of identical offers re-scraped in different waves; yields a defensible noise magnitude per criterion" # [NOVEL] artifact F_noise : feature(D_offers) value structure: list value type: numeric description: "Estimated value uncertainty per criterion, in natural units (e.g., ±20 EUR rent, ±2 m2 living space)" transform T_set_tolerance : intent: generate-knowledge manner: "set per-criterion tolerance thresholds seeded by noise estimates" input: F_noise, S_criteria output: S_tol actor: hybrid description: "Tolerances epsilon_i are initialised from measured noise and adjustable by the analyst in natural units" artifact S_tol : specification representation form: "parameter settings" description: "Per-criterion tolerance thresholds for tolerant dominance" # ============================================================================ # STAGE 5: DECISION CONTEXTS # [NOVEL] # machine proposes groups -> machine describes as rules -> human legislates # ============================================================================ artifact S_group_params : specification origin: given representation form: "parameter settings" description: "Number of archetypes/components, normalisation scheme" loop L_context: purpose: "Establish interpretable, predicate-based decision contexts separating incommensurable option types (e.g., studios vs. family flats vs. premium objects)" until: "Contexts are interpretable, well-covering, and accepted by analyst" body: transform T_propose_groups : intent: model manner: "archetypal analysis / NMF over normalised criteria values" input: D_cand, S_criteria, S_group_params output: M_groups, F_member actor: machine description: "Identify candidate option archetypes; soft memberships expose fuzzy boundaries between option types" artifact M_groups : model(D_cand) model type: "matrix factorisation / archetype model" representation form: "factor matrices (W,H) or archetype set" description: "Data-driven proposal of option groupings" artifact F_member : feature(D_cand) value structure: vector value type: numeric description: "Soft membership degrees of options in proposed groups" transform T_describe_groups : intent: abstract manner: "rule induction approximating group memberships" input: M_groups, F_member, D_cand output: P_rules actor: machine description: "Translate opaque factor/cluster structure into human-readable predicates over criteria" # [NOVEL] artifact P_rules : pattern(M_groups, F_member) representation form: "predicates over criteria" description: "Candidate group descriptions, e.g., 'livingSpace > 120 AND noRooms >= 4'" transform T_define_contexts : intent: generate-knowledge manner: "edit, name, merge, split rule-based definitions" input: P_rules, V_attrs output: S_ctx actor: human description: "Analyst converts proposals into named, persistent, editable decision-context definitions" # [NOVEL] artifact S_ctx : specification representation form: "rules" description: "Named predicate-defined decision contexts (e.g., 'studios', 'family flats', 'premium')" transform T_partition : intent: define-unit manner: "rule-based grouping into decision contexts" input: D_cand, S_ctx output: D_ctx, F_ctx_label actor: machine description: "Assign candidate options to decision contexts" artifact D_ctx : entities internal structure: group/cluster embedment: set features: - id: ctx_size value structure: atomic value type: numeric description: "Number of options in context" description: "Decision contexts as groups of candidate options" artifact F_ctx_label : feature(D_cand) value structure: atomic value type: categorical description: "Decision-context membership per option (incl. 'unassigned' residual)" transform T_vis_contexts : intent: visualise manner: "map and feature-space small multiples per context" input: D_cand, F_ctx_label, F_member output: V_ctx actor: machine description: "Show spatial distribution and criteria profiles of contexts; expose residuals and boundary options whose soft memberships disagree with crisp rules" artifact V_ctx : visualisation(F_ctx_label, F_member) layout: "map + small multiples of histograms per context" form: "coloured point marks; histograms" encoding: "colour: context; saturation: membership confidence" description: "Context overview for validation and editing" transform T_assess_contexts : intent: assess manner: "evaluate coverage, overlap, interpretability" input: V_ctx, D_ctx output: ctx_assessment actor: human description: "Analyst judges whether contexts form a meaningful, sufficiently covering decomposition" artifact ctx_assessment : knowledge(D_ctx) representation form: "quality judgment" description: "Judgment on context decomposition quality" if ctx_assessment indicates refinement needed: then: assign: S_group_params := S_group_params' # or direct edit of S_ctx else: exit loop L_context end loop L_context # ============================================================================ # STAGE 6: TOLERANT DOMINANCE AND SENSITIVITY # [NOVEL] # ============================================================================ loop L_rank: purpose: "Compute graded tolerant-Pareto relevance per context and probe its sensitivity to tolerances, utilities, and context definitions" until: "Frontiers are informative (neither degenerate nor exploded) and stable under plausible specification variations" body: transform T_dominance : intent: characterise manner: "epsilon-tolerant dominance within each decision context over utility-transformed criteria including distance; robustness margin per option; optimistic/pessimistic bounding for missing values" input: D_cand, F_ctx_label, F_util, F_distance, S_criteria, S_tol output: F_robust, F_front_status actor: machine description: "Replace the binary Pareto set by a graded robustness feature: the signed smallest tolerance change that flips an option's frontier membership. Frontier defined as 'not tolerantly dominated by any option of the same context' (well-defined despite non-transitivity). Distance participates in the dominance relation. Missing values yield a certain/possible/dominated status via best/worst-case bounding" # [NOVEL] artifact F_robust : feature(D_cand) value structure: atomic value type: numeric description: "Frontier robustness margin per option (graded relevance)" artifact F_front_status : feature(D_cand) value structure: atomic value type: categorical description: "certainly-on-frontier / possibly-on-frontier / dominated (under missing-value bounding)" artifact D_geo : entities origin: given internal structure: elementary embedment: space description: "Geographic base map serving as spatial context" transform T_map : intent: contextualise manner: "geographic positioning" input: D_cand, D_geo output: A_map actor: machine description: "Arrange candidate options on the geographic map" artifact A_map : arrangement(D_cand) context: D_geo principle: "geographic coordinates" description: "Map arrangement of candidate options" transform T_vis_rank : intent: visualise manner: "coordinated map, parallel coordinates and ranking table" input: A_map, F_robust, F_front_status, F_ctx_label, F_util output: V_rank actor: machine description: "Coordinated views faceted by decision context; robustness encoded continuously; interactive thresholding" artifact V_rank : visualisation(A_map, F_robust, F_front_status, F_util) layout: "map + parallel coordinates + table, faceted by context" form: "point marks; polylines; table rows" encoding: "position: map/axes; colour+opacity: robustness margin; shape/outline: frontier status; facet: context" description: "Per-context tolerant frontiers with graded relevance" transform T_vis_stability : intent: visualise manner: "frontier stability curves" input: F_robust, S_tol, F_ctx_label output: V_stab actor: machine description: "Show frontier size and membership changes as functions of tolerance scaling per context; sensitivity view" artifact V_stab : visualisation(F_robust, S_tol) layout: "tolerance scaling axis per context" form: "step curves with membership-change marks" encoding: "x: tolerance scale factor; y: frontier size; marks: options entering/leaving" description: "Frontier stability under tolerance variation" transform T_assess_rank : intent: assess manner: "evaluate informativeness and stability of frontiers" input: V_rank, V_stab output: rank_assessment actor: human description: "Analyst judges whether frontiers discriminate usefully and which specifications require adjustment" artifact rank_assessment : knowledge(F_robust) representation form: "quality judgment" description: "Judgment on frontier informativeness and stability" if rank_assessment indicates refinement needed: then: transform T_adjust : intent: generate-knowledge manner: "revise tolerances, utilities, or context definitions" input: rank_assessment, V_stab, S_tol, S_util, S_ctx output: S_tol', S_util', S_ctx' actor: human description: "Targeted specification adjustment; context edits re-enter via L_context, utility edits via L_utility" assign: S_tol := S_tol' S_util := S_util' S_ctx := S_ctx' else: exit loop L_rank end loop L_rank # ============================================================================ # STAGE 7: INTERPRETATION AND DECISION # ============================================================================ transform T_tradeoffs : intent: abstract manner: "interpret characteristic trade-offs per context" input: V_rank, V_stab, F_robust output: P_trade actor: human description: "Identify recurring trade-off structures among robust options within each context (e.g., distance vs. rent vs. space)" artifact P_trade : pattern(F_robust, F_ctx_label) representation form: "textual descriptions" description: "Characteristic trade-off patterns per decision context" transform T_decide : intent: generate-knowledge manner: "formulate shortlist with sensitivity statements" input: P_trade, V_rank, V_stab output: K_decision actor: human description: "Synthesize per-context shortlists of robust options, cross-context comparison of best representatives, and explicit statements about result sensitivity to tolerances, utilities, and context definitions" artifact K_decision : knowledge(P_trade, F_robust) representation form: "ranking and statements" description: "Robust per-context shortlists with documented sensitivity; cross-context comparison supporting the final choice"
Decision contexts as editable specifications (Stage 5). The chain
model → abstract (rule induction) → generate-knowledge (human editing) → define-unit
makes group definitions first-class, persistent, human-legislated artifacts.
Closest library precedent: Utopian (human-steered NMF) steers the model;
here the human steers predicate definitions derived from the model, and the
model becomes disposable scaffolding. Related DB work (group-by skylines)
has no interactive definition mechanism.
Graded tolerant frontier (Stage 6, F_robust). Replaces the binary
skyline with a per-option robustness margin; subsumes both relaxation
directions (tolerance against frontier explosion and against false
exclusion) under one continuous, thresholdable encoding. Direct descendant
of the robustness-probing agenda of Andrienko & Andrienko (Inf. Vis. 2003),
now applied to the dominance structure itself.
Data-grounded tolerances (Stage 4, F_noise → S_tol). Tolerance
thresholds are measured from cross-wave duplicate variation rather than
assumed — an empirically defensible operationalisation of "tolerating
Pareto optimality because of data imperfections".
Missing-value bounding (F_front_status). Certain/possible frontier
membership via optimistic/pessimistic completion addresses incompleteness
without the non-transitivity pathologies of observed-subset dominance.
V_ctx.T_decide), not formalised.