Skip to content

QML Syntax

QML (Questionnaire Markup Language) is a YAML-based language for creating formally verified questionnaires. This page provides a brief technical overview.

Overview

QML enables:

  • Formal verification of questionnaire logic using SMT solvers
  • Conditional branching with preconditions and postconditions
  • Integer-based outcomes for all responses, enabling mathematical analysis
  • Embedded Python code for computations (restricted subset for verification)

Key Components

Structure

qmlVersion: "1.0"
questionnaire:
  title: "Survey Title"
  codeInit: |
    # Python initialization code
  blocks:
    - id: b_section
      items:
        - id: q_item
          kind: Question
          precondition:
            - predicate: condition
          postcondition:
            - predicate: validation
          input:
            control: ControlType

Item Types

  • Comment: Display text without collecting responses
  • Question: Single integer outcome
  • QuestionGroup: Vector of integer outcomes
  • MatrixQuestion: Matrix of integer outcomes

Input Controls

  • Switch: Binary (0/1)
  • Radio: Single selection from labeled options
  • Checkbox: Multiple selection (bit mask encoding)
  • Dropdown: Single selection with prefix/suffix
  • Editbox: Free-form integer within [min, max]
  • Slider: Visual integer selection
  • Range: Interval selection (two integers encoded via Szudzik pairing)

Conditional Logic

Preconditions determine visibility:

precondition:
  - predicate: q_age.outcome >= 18

Postconditions enforce constraints:

postcondition:
  - predicate: q_children.outcome < q_household.outcome
    hint: "Children must be fewer than household size"

Code Blocks

Restricted Python subset for formal verification:

Supported: - Arithmetic: +, -, *, // - Comparisons: <, <=, >, >=, ==, != - Boolean: and, or, not - Control: if/elif/else, for (limited) - Outcome access: item.outcome

Not supported: - Functions, classes, imports - While loops, break, continue - Built-ins (except range) - Dictionaries, strings methods

Complete Examples

This section demonstrates all item kinds and control types with working examples.

Item Kind: Comment

Display informational text without collecting responses:

- id: intro_comment
  kind: Comment
  title: "Welcome to our demographic survey. Your responses help us understand our community better."

Comments can have conditional display:

- id: parent_notice
  kind: Comment
  title: "The following questions are about your children."
  precondition:
    - predicate: q_has_children.outcome == 1

Item Kind: Question

Single-outcome questions with various control types.

Control: Switch

Binary choice (0 = off, 1 = on):

- id: q_has_children
  kind: Question
  title: "Do you have children?"
  input:
    control: Switch
    off: "No"
    on: "Yes"
    default: 0

Control: Radio

Single selection from labeled options:

- id: q_education
  kind: Question
  title: "What is your highest level of education?"
  input:
    control: Radio
    labels:
      1: "High School"
      2: "Bachelor's Degree"
      3: "Master's Degree"
      4: "Doctorate"
    default: 1

Control: Dropdown

Single selection with optional prefix/suffix text:

- id: q_country
  kind: Question
  title: "Country of residence"
  input:
    control: Dropdown
    left: "I live in"
    right: ""
    labels:
      1: "United States"
      2: "Canada"
      3: "United Kingdom"
      4: "Germany"
      5: "Other"

Control: Checkbox

Multiple selection (bit mask encoding):

- id: q_interests
  kind: Question
  title: "Select all that apply:"
  input:
    control: Checkbox
    labels:
      1: "Reading"       # Bit 0: value 2^0 = 1
      2: "Sports"        # Bit 1: value 2^1 = 2
      4: "Music"         # Bit 2: value 2^2 = 4
      8: "Travel"        # Bit 3: value 2^3 = 8
      16: "Cooking"      # Bit 4: value 2^4 = 16

Selecting "Reading" and "Music" produces outcome: 1 | 4 = 5

Control: Editbox

Free-form integer input with bounds:

- id: q_age
  kind: Question
  title: "What is your age?"
  input:
    control: Editbox
    min: 0
    max: 120
    left: ""
    right: "years old"
    default: 25
  postcondition:
    - predicate: q_age.outcome >= 18
      hint: "You must be 18 or older to participate"

Control: Slider

Visual integer selection:

- id: q_satisfaction
  kind: Question
  title: "How satisfied are you with our service?"
  input:
    control: Slider
    min: 0
    max: 10
    step: 1
    left: "Not satisfied"
    right: "Very satisfied"
    labels:
      0: "0"
      5: "5"
      10: "10"
    default: 5

Control: Range

Interval selection (two integers encoded as one):

- id: q_price_range
  kind: Question
  title: "What is your acceptable price range?"
  input:
    control: Range
    min: 0
    max: 1000
    step: 50
    left: "$"
    right: ""

The outcome is encoded via Szudzik pairing. Selecting \([100, 500]\) produces a single integer.

Item Kind: QuestionGroup

Vector of outcomes with identical control for each sub-question:

- id: qg_family_ages
  kind: QuestionGroup
  title: "Please enter the age of each family member:"
  questions:
    - "Yourself"
    - "Spouse"
    - "First child"
    - "Second child"
  precondition:
    - predicate: q_household_size.outcome >= 2
  input:
    control: Editbox
    min: 0
    max: 120
    right: "years"
  postcondition:
    - predicate: qg_family_ages.outcome[0] >= 18
      hint: "Primary respondent must be 18 or older"

Access outcomes via indexing: qg_family_ages.outcome[0], qg_family_ages.outcome[1], etc.

Item Kind: MatrixQuestion

Matrix of outcomes (rows × columns):

- id: mq_language_proficiency
  kind: MatrixQuestion
  title: "Please indicate the language proficiency level for each family member:"
  rows:
    - "English"
    - "Spanish"
    - "French"
    - "German"
    - "Mandarin"
  columns:
    - "Yourself"
    - "Spouse"
    - "First Child"
    - "Second Child"
  input:
    control: Dropdown
    labels:
      0: "None"
      1: "Beginner"
      2: "Intermediate"
      3: "Advanced"
      4: "Native"

Access outcomes via row/column indexing: mq_language_proficiency.outcome[0][1] for first language (English), second family member (Spouse).

The matrix creates a grid where each cell represents one family member's proficiency in one language. This example produces a 5×4 matrix (5 languages × 4 family members) = 20 individual outcomes.

Complex Example with Code Blocks

Demonstrating preconditions, postconditions, and code blocks:

qmlVersion: "1.0"
questionnaire:
  title: "Income Survey"
  codeInit: |
    total_income = 0
  blocks:
    - id: b_demographics
      items:
        - id: q_employment
          kind: Question
          title: "Are you currently employed?"
          input:
            control: Switch
            off: "No"
            on: "Yes"

        - id: q_income
          kind: Question
          title: "What is your annual income?"
          precondition:
            - predicate: q_employment.outcome == 1
          input:
            control: Editbox
            min: 0
            max: 1000000
            left: "$"
            right: "per year"
          codeBlock: |
            total_income = q_income.outcome

        - id: q_spouse_employed
          kind: Question
          title: "Is your spouse employed?"
          precondition:
            - predicate: q_employment.outcome == 1
          input:
            control: Switch
            off: "No"
            on: "Yes"

        - id: q_spouse_income
          kind: Question
          title: "What is your spouse's annual income?"
          precondition:
            - predicate: q_spouse_employed.outcome == 1
          input:
            control: Editbox
            min: 0
            max: 1000000
            left: "$"
            right: "per year"
          codeBlock: |
            total_income = total_income + q_spouse_income.outcome

        - id: q_household_income
          kind: Question
          title: "What is your total household income?"
          input:
            control: Editbox
            min: 0
            max: 2000000
            left: "$"
            right: "per year"
          postcondition:
            - predicate: q_household_income.outcome >= total_income
              hint: "Household income cannot be less than reported individual incomes"

This example demonstrates:

  • Conditional visibility: Income questions only appear if employed
  • Code blocks: Track cumulative income across questions
  • Postcondition validation: Ensure household income is consistent with individual incomes
  • Chained dependencies: Each question depends on previous answers

Mathematical Semantics

Each item has associated outcome variable(s):

  • Question: \(S_i \in [\text{min}, \text{max}]\)
  • QuestionGroup: \(\mathbf{S}_i \in \mathbb{Z}^k\)
  • MatrixQuestion: \(\mathbf{S}_i \in \mathbb{Z}^{m \times n}\)

Special encodings:

  • Checkbox: Multiple selection encoded as bit mask (OR of powers of 2)
  • Range: Interval \((a, b)\) encoded via Szudzik pairing:
\[ \text{pair}(a, b) = \begin{cases} a^2 + a + b & \text{if } a \geq b \\ a + b^2 & \text{if } a < b \end{cases} \]

This bijection \(\mathbb{Z} \times \mathbb{Z} \to \mathbb{Z}\) represents two integers as one while remaining decodable.

Preconditions and postconditions are Boolean predicates over outcome variables.

The system validates:

\[ B \wedge \bigwedge_{i=1}^n (P_i \Rightarrow Q_i) \]

where \(B\) encodes all domain constraints.

See Questionnaire Analysis for formal treatment.

Further Reading