Using Scenic Programmatically

While Scenic is most easily invoked as a command-line tool, it also provides a Python API for compiling Scenic programs, sampling scenes from them, and running dynamic simulations.

The top-level interface to Scenic is provided by two functions in the scenic module which compile a Scenic program:

scenarioFromFile(path, params={}, model=None, scenario=None, cacheImports=False)[source]

Compile a Scenic file into a Scenario.

Parameters
  • path (str) – Path to a Scenic file.

  • params (dict) – Global parameters to override, as a dictionary mapping parameter names to their desired values.

  • model (str) – Scenic module to use as world model.

  • scenario (str) – If there are multiple modular scenarios in the file, which one to compile; if not specified, a scenario called ‘Main’ is used if it exists.

  • cacheImports (bool) – Whether to cache any imported Scenic modules. The default behavior is to not do this, so that subsequent attempts to import such modules will cause them to be recompiled. If it is safe to cache Scenic modules across multiple compilations, set this argument to True. Then importing a Scenic module will have the same behavior as importing a Python module. See purgeModulesUnsafeToCache for a more detailed discussion of the internals behind this.

Returns

A Scenario object representing the Scenic scenario.

scenarioFromString(string, params={}, model=None, scenario=None, filename='<string>', cacheImports=False)[source]

Compile a string of Scenic code into a Scenario.

The optional filename is used for error messages. Other arguments are as in scenarioFromFile.

The resulting Scenario object represents the abstract scenario defined by the Scenic program. To sample concrete scenes from this object, you can call the Scenario.generate method, which returns a Scene. If you are only using static scenarios, you can extract the sampled values for all the global parameters and objects in the scene from the Scene object. For example:

import random, scenic
random.seed(12345)
scenario = scenic.scenarioFromString('ego = Object with foo Range(0, 5)')
scene, numIterations = scenario.generate()
print(f'ego has foo = {scene.egoObject.foo}')
ego has foo = 2.083099362726706

To run dynamic scenarios, you must instantiate an instance of the Simulator class for the particular simulator you want to use. Each simulator interface that supports dynamic simulations defines a subclass of Simulator; for example, NewtonianSimulator for the simple Newtonian simulator built into Scenic. These subclasses provide simulator-specific functionality, and have different requirements for their use: see the specific documentation of each interface under scenic.simulators for details.

Once you have an instance of Simulator, you can ask it to run a simulation from a Scene by calling the Simulator.simulate method. If Scenic is able to run a simulation that satisfies all the requirements in the Scenic program (potentially after multiple attempts – Scenic uses rejection sampling), this method will return a Simulation object. Results of the simulation can then be obtained by inspecting its result attribute, which is an instance of SimulationResult (simulator-specific subclasses of Simulation may also provide additional information). For example:

import scenic
from scenic.simulators.newtonian import NewtonianSimulator
scenario = scenic.scenarioFromFile('examples/driving/badlyParkedCarPullingIn.scenic',
                                   model='scenic.simulators.newtonian.driving_model')
scene, _ = scenario.generate()
simulator = NewtonianSimulator()
simulation = simulator.simulate(scene, maxSteps=10)
if simulation:  # `simulate` can return None if simulation fails
        result = simulation.result
        for i, state in enumerate(result.trajectory):
                egoPos, parkedCarPos = state
                print(f'Time step {i}: ego at {egoPos}; parked car at {parkedCarPos}')

If you want to monitor data from simulations to see if the system you are testing violates its specfications, you may want to use VerifAI instead of implementing your own code along the lines above. VerifAI supports running tests from Scenic programs, specifying system specifications using temporal logic or arbitrary Python monitor functions, actively searching the space of parameters in a Scenic program to find concrete scenarios where the system violates its specs 1, and more. See the VerifAI documentation for details.

See also

If you get exceptions or unexpected behavior when using the API, Scenic provides various debugging features: see Debugging.

1

VerifAI’s active samplers can be used directly from Scenic when VerifAI is installed. See scenic.core.external_params.