How Solver queries name features

A Solver query, the expression you pass to evaluate --expr or check --predicate, is written in SysML expression syntax, but a qualified name inside it means something SysML itself has no notation for. This page explains that meaning and why the Solver needs it.

A qualified name designates a declared feature

In a query, a qualified name such as Catalog::Wheel::mass designates the feature as it is declared, and the Solver reports the values that feature can take in every situation the model allows. The feature may belong to a definition that has no usages at all. Create catalog.sysml:

package Catalog {
    part def Wheel {
        attribute rimMass = 9;
        attribute tyreMass = 3;
        attribute mass = rimMass + tyreMass;
    }
}

Nothing in this model is typed by Wheel, yet the question “what can a wheel’s mass be?” has an answer:

syside solver evaluate -i catalog.sysml --expr "Catalog::Wheel::mass"
{
  "result": {
    "subject": "Catalog",
    "expression": "Catalog::Wheel::mass",
    "determination": "determined",
    "outcome": {
      "shape": "attainable_set",
      "attainable": {
        "values": [
          {
            "value": "12",
            "witness": {
              "values": [
                ["Catalog::Wheel::rimMass", "9"],
                ["Catalog::Wheel::tyreMass", "3"],
                ["Catalog::Wheel::mass", "12"]
              ],
              "value": "12"
            }
          }
        ],
        "exhaustive": "true"
      },
      "unattainable": { "values": [], "exhaustive": "false" }
    },
    "causes": []
  },
  "warnings": [],
  "exit_code": 0
}

A path names a feature reached from an arbitrary instance New in v1.0.0

A feature is often reached through other features: the mass of a rocket’s booster, not the mass of boosters in general. Write that question as a path, Q::f1.f2: the part before the last :: names a type Q, and the .-separated chain after it is read from an arbitrary instance of Q. Launch::Rocket::booster.mass means “booster.mass of an arbitrary Rocket”, whether or not the model contains a usage typed by Rocket. It is the value the expression booster.mass would have if it were written inside Rocket.

Create launch.sysml:

package Launch {
    private import ScalarValues::Integer;

    part def Booster {
        attribute dry : Integer = 30;
        attribute fuel : Integer;
        attribute mass : Integer = dry + fuel;
    }
    part def Rocket {
        part booster : Booster {
            attribute redefines fuel = 10;
        }
    }
}

Every booster weighs dry + fuel, and a rocket’s booster carries 10 units of fuel:

syside solver evaluate -i launch.sysml --expr "Launch::Rocket::booster.mass"
{
  "result": {
    "subject": "Launch",
    "expression": "Launch::Rocket::booster.mass",
    "determination": "determined",
    "outcome": {
      "shape": "attainable_set",
      "attainable": {
        "values": [
          {
            "value": "40",
            "witness": {
              "values": [
                ["Launch::Rocket::booster.fuel", "10"],
                ["Launch::Rocket::booster.dry", "30"],
                ["Launch::Rocket::booster.mass", "40"]
              ],
              "value": "40"
            }
          }
        ],
        "exhaustive": "true"
      },
      "unattainable": { "values": [], "exhaustive": "false" }
    },
    "causes": []
  },
  "warnings": [],
  "exit_code": 0
}

The Solver names every value in its output the same way, so the witness reports Launch::Rocket::booster.fuel, the fuel of the booster of the arbitrary rocket.

Several paths in one query start from the same instance. In Launch::Rocket::booster.mass == Launch::Rocket::booster.dry + 10, both sides belong to one rocket, exactly as they would in an attribute written inside Rocket.

A qualified name through an inherited feature is refused New in v1.0.0

Launch::Rocket::booster::mass looks like the same question, but SysML name resolution reads it differently. The rocket’s booster declares nothing called mass: it inherits mass from Booster, and :: resolves an inherited name to the declaration it came from. As a qualified name, Launch::Rocket::booster::mass is Launch::Booster::mass, the mass of an arbitrary Booster, whose fuel is anything at all. Both questions are reasonable, so the Solver takes neither silently:

syside solver evaluate -i launch.sysml --expr "Launch::Rocket::booster::mass"
{
  "error": {
    "code": "Load",
    "span": null,
    "message": "'Launch::Rocket::booster::mass' passes through 'mass', which 'Launch::Rocket::booster' inherits without redefining; as a qualified name it means 'Launch::Booster::mass', not a path through 'Launch::Rocket::booster'. Write the question as one of:\n  Launch::Rocket::booster.mass  (booster.mass of an arbitrary Launch::Rocket)\n  Launch::Booster::mass         (mass of an arbitrary Launch::Booster)"
  },
  "exit_code": 2
}

Changed in version 1.0.0: Before 1.0.0, the Solver read such a name as the path, without saying so, and named every value in its output with :: alone (Launch::Rocket::booster::mass).

The refusal lists every question the name could stand for, each with its reading. Pick one and rerun. A qualified name whose features are each declared or redefined by the one before it, such as Catalog::Wheel::mass above, has only one reading and is accepted as written.

Why the Solver defines its own notation

The question is well defined in SysML v2. A feature declared in a definition is interpreted over every instance of that definition, whether or not the model contains a usage of it (KerML 1.0, clause 8.4.3.4). “The values Wheel::mass can take” is therefore a sound concept.

The SysML v2 specification and its standard library give no notation for it (see Differences to the Pilot Implementation for how Syside relates to both). A SysML feature reference is evaluated on an instance: the referenced feature must be accessible from where the expression is written (KerML 1.0, clauses 8.3.4.8.5 and 8.3.3.3.10). Written at package level, Catalog::Wheel::mass has no Wheel in context, so as a SysML expression it is not valid. Chain notation (someWheel.mass) needs an instance to start from, and adding one would change the model being asked about. No construct in the specification denotes “this feature, across all instances of its definition” without introducing an instance.

The Solver therefore defines its own notation. In a query, a qualified name is resolved by the specification’s ordinary name resolution to a declared feature, and stands for that feature’s values across all situations the model allows. A path Q::f1.f2 stands for the values of the chain f1.f2 read from an arbitrary instance of Q.

Where the notation applies

The notation applies to query text only. Inside a model file, the same text is an ordinary SysML expression, and Syside validates it as one: Syside 1.0 and later report feature-reference-expression-access (“Feature reference must be accessible from the outer scope”) for such a reference. The Solver does not apply that check to the query you pass on the command line.