Requirement Satisfaction Evaluation v0.9.0 Labs

Experimental feature

This example uses experimental unit conversion functionality. Enable with experimental_quantities=True.

This example evaluates whether each RequirementUsage in a model is satisfied.

The model defines reusable requirement def declarations parameterized by a subject and typed attributes, declares them on part def Bicycle, and redefines their attributes on each variant in a variation part def. The bicycle variants cover three outcomes: passing, failing a requirement, and a case where a violated assume constraint makes the requirement not apply.

Model Structure

RequirementSatisfactionExample
├── Bicycle
│   ├── requirement massOk            (mass <= 12 kg)
│   └── requirement wheelOk           (600 <= wheelDiameter <= 800 mm)
├── TandemBike :> Bicycle
│   ├── requirement massOk            (mass <= 25 kg)
│   └── requirement wheelOk           (600 <= wheelDiameter <= 800 mm)
└── BicycleSelection
    ├── roadBike : Bicycle            (mass =  9 kg, wheelDiameter = 700 mm)
    ├── heavyBike : Bicycle           (mass = 18 kg, wheelDiameter = 750 mm)
    ├── impossibleBike : TandemBike   (mass = 20 kg, wheelDiameter =-500 mm)
    └── ChildBikes
        └── smallBike : Bicycle       (mass =  7 kg, wheelDiameter = 500 mm)

Each variant binds its own subject explicitly via a scope override (requirement :>> massOk { subject bike = roadBike; }). Requirements declared on part def Bicycle and part def TandemBike are blueprints: their expressions reference each definition’s own parameters rather than the features of a concrete usage, so they cannot be evaluated.

Assumed and Required Constraints

Each requirement def is built from two parts: a require constraint that must hold for the requirement to be satisfied, and an optional assume constraint that guards when the requirement applies. If the assume constraint is False, the requirement does not apply, and the evaluator reports True without checking the require constraint.

requirement def MassLimit {
    subject bike : Bicycle;
    attribute limit : MassValue;
    assume constraint { bike.mass > 0 [kg] }
    require constraint { bike.mass <= limit }
}

WheelSizeRange follows the same pattern, with wheelDiameter > 0 [mm] as its assume and minDiameter <= wheelDiameter <= maxDiameter as its require.

The impossibleBike variant exercises the assume guard. Its wheelDiameter = -500 [mm] violates bike.wheelDiameter > 0 [mm], so wheelOk evaluates to True even though -500 [mm] is well outside [600, 800] [mm].

Warning

A passing requirement does not always mean the require constraint holds. it may mean the assume constraint failed and the requirement did not apply.

When auditing results, verify that the assume conditions actually held.

Selecting Requirements to Evaluate

A RequirementUsage whose owning_type is a Definition is a blueprint and is excluded. Requirements owned by another Usage (a variant part, in this model) are bound to that owning usage and can access its features; each variant in this model also supplies an explicit subject binding via scope override, so evaluation can proceed. Composite sub-requirements nested inside another RequirementUsage are also skipped because the outer requirement’s verdict already aggregates them:

requirements = [
    req
    for req in model.elements(syside.RequirementUsage, include_subtypes=True)
    if req.document.document_tier is syside.DocumentTier.Project
    and isinstance(req.owning_type, syside.Usage)
    and (
        not req.is_composite or not isinstance(req.owning_type, syside.RequirementUsage)
    )
]

Evaluating a Requirement

Compiler.evaluate reduces a requirement to a boolean, honoring the assume guard, with the subject and attribute redefinitions resolved from the usage’s own scope:

value, report = compiler.evaluate(
    req, stdlib=STANDARD_LIBRARY, experimental_quantities=True
)

The experimental_quantities=True flag enables automatic unit conversion, so a constraint comparing [kg] to [kg] or [mm] to [mm] is evaluated numerically.

Concepts Used

SysML v2 requirement concepts:

Concept

Syntax

Description

Requirement definition

requirement def

Reusable requirement parameterized by a subject and attributes

Subject parameter

subject bike : Bicycle

The element being constrained; bound at the usage site

Required constraint

require constraint { <expr> }

Boolean expression that must hold for the requirement to be satisfied

Assumed constraint

assume constraint { <expr> }

Precondition under which the requirement applies

Variation / variant

variation part def / variant part

A definition with discrete alternatives, each a concrete Usage

Redefinition

:>> name

Redefines an inherited feature (here, to bind subject or override an attribute)

Quantity comparison

bike.mass <= limit

Compares quantity-typed attributes; with experimental_quantities=True the compiler converts mismatched units automatically

Syside API:

API

Purpose

Compiler.evaluate()

Evaluates a RequirementUsage to a boolean, honoring the assume guard, with the subject and attribute redefinitions resolved from the usage’s scope

Model.elements()

Iterates over semantic elements, optionally including subtypes

Feature.owning_type

The Type this feature is a member of (a Usage or Definition)

Example Model

package RequirementSatisfactionExample {
    private import ScalarValues::*;
    private import SI::*;

    requirement def MassLimit {
        subject bike : Bicycle;
        attribute limit : MassValue;
        assume constraint { bike.mass > 0 [kg] }
        require constraint { bike.mass <= limit }
    }

    requirement def WheelSizeRange {
        subject bike : Bicycle;
        attribute minDiameter : LengthValue;
        attribute maxDiameter : LengthValue;
        assume constraint { bike.wheelDiameter > 0 [mm] }
        require constraint {
            (bike.wheelDiameter >= minDiameter)
                and (bike.wheelDiameter <= maxDiameter)
        }
    }

    // Requirements declared on `Bicycle` and `TandemBike` are blueprints:
    // their expressions reference each definition's own parameters rather
    // than the features of a concrete usage, so they cannot be evaluated.
    part def Bicycle {
        attribute mass : MassValue;
        attribute wheelDiameter : LengthValue;

        requirement massOk : MassLimit {
            attribute :>> limit default 12 [kg];
        }
        requirement wheelOk : WheelSizeRange {
            attribute :>> minDiameter default 600 [mm];
            attribute :>> maxDiameter default 800 [mm];
        }
    }

    part def TandemBike :> Bicycle {
        requirement :>> massOk {
            attribute :>> limit = 25 [kg];
        }
    }

    variation part def BicycleSelection {
        variant part roadBike : Bicycle {
            attribute :>> mass = 9 [kg];
            attribute :>> wheelDiameter = 700 [mm];
            requirement :>> massOk { subject bike = roadBike; }
            requirement :>> wheelOk { subject bike = roadBike; }
        }

        variant part heavyBike : Bicycle {
            attribute :>> mass = 18 [kg];
            attribute :>> wheelDiameter = 750 [mm];
            requirement :>> massOk { subject bike = heavyBike; }
            requirement :>> wheelOk { subject bike = heavyBike; }
        }

        variant part impossibleBike : TandemBike {
            attribute :>> mass = 20 [kg];
            requirement :>> massOk { subject bike = impossibleBike; }

             // Unrealistic value
            attribute :>> wheelDiameter = -500 [mm];
             // Requirement check passes as assume constraint is violated
            requirement :>> wheelOk { subject bike = impossibleBike; }
        }

        variation part def ChildBikes {
            variant part smallBike : Bicycle {
                attribute :>> mass = 7 [kg];
                attribute :>> wheelDiameter = 500 [mm];
                requirement :>> massOk { subject bike = smallBike; }
                requirement :>> wheelOk { subject bike = smallBike; }
            }
        }
    }
}

Example Script

import pathlib

import syside

EXAMPLE_DIR = pathlib.Path(__file__).parent
MODEL_FILE_PATH = EXAMPLE_DIR / "example_model.sysml"
STANDARD_LIBRARY = syside.Environment.get_default().lib


def main() -> None:
    (model, _) = syside.load_model([MODEL_FILE_PATH], warnings_as_errors=True)
    compiler = syside.Compiler()

    # Skip requirements owned by a `Definition` (expressions reference
    # the definition's own parameters) and composites within another
    # `RequirementUsage` (already folded into the parent's verdict).
    requirements = [
        req
        for req in model.elements(
            syside.RequirementUsage, include_subtypes=True
        )
        if req.document.document_tier is syside.DocumentTier.Project
        and isinstance(req.owning_type, syside.Usage)
        and (
            not req.is_composite
            or not isinstance(req.owning_type, syside.RequirementUsage)
        )
    ]

    for req in requirements:
        value, report = compiler.evaluate(
            req, stdlib=STANDARD_LIBRARY, experimental_quantities=True
        )
        if report.fatal or not isinstance(value, bool):
            marker = "[ ?? ]"
        else:
            marker = "[ OK ]" if value else "[FAIL]"
        print(f"  {marker} {req}")


if __name__ == "__main__":
    main()

Output

[ OK ] RequirementSatisfactionExample::BicycleSelection::roadBike::massOk
[ OK ] RequirementSatisfactionExample::BicycleSelection::roadBike::wheelOk
[FAIL] RequirementSatisfactionExample::BicycleSelection::heavyBike::massOk
[ OK ] RequirementSatisfactionExample::BicycleSelection::heavyBike::wheelOk
[ OK ] RequirementSatisfactionExample::BicycleSelection::impossibleBike::massOk
[ OK ] RequirementSatisfactionExample::BicycleSelection::impossibleBike::wheelOk
[ OK ] RequirementSatisfactionExample::BicycleSelection::ChildBikes::smallBike::massOk
[FAIL] RequirementSatisfactionExample::BicycleSelection::ChildBikes::smallBike::wheelOk

Download

Download this example here.