Пример #1
0
	def validate(self):
		"""Make some simple static checks for inconsistent built-in requirements.

		:meta private:
		"""
		objects = self.objects
		staticVisibility = self.egoObject and not needsSampling(self.egoObject.visibleRegion)
		staticBounds = [self.hasStaticBounds(obj) for obj in objects]
		for i in range(len(objects)):
			oi = objects[i]
			container = self.containerOfObject(oi)
			# Trivial case where container is empty
			if isinstance(container, EmptyRegion):
				raise InvalidScenarioError(f'Container region of {oi} is empty')
			# skip objects with unknown positions or bounding boxes
			if not staticBounds[i]:
				continue
			# Require object to be contained in the workspace/valid region
			if not needsSampling(container) and not container.containsObject(oi):
				raise InvalidScenarioError(f'Object at {oi.position} does not fit in container')
			# Require object to be visible from the ego object
			if staticVisibility and oi.requireVisible is True and oi is not self.egoObject:
				if not self.egoObject.canSee(oi):
					raise InvalidScenarioError(f'Object at {oi.position} is not visible from ego')
			# Require object to not intersect another object
			for j in range(i):
				oj = objects[j]
				if not staticBounds[j]:
					continue
				if oi.intersects(oj):
					raise InvalidScenarioError(f'Object at {oi.position} intersects'
											   f' object at {oj.position}')
Пример #2
0
def model(namespace, modelName):
    global loadingModel
    if loadingModel:
        raise RuntimeParseError(
            f'Scenic world model itself uses the "model" statement')
    if lockedModel is not None:
        modelName = lockedModel
    try:
        loadingModel = True
        module = importlib.import_module(modelName)
    except ModuleNotFoundError as e:
        if e.name == modelName:
            raise InvalidScenarioError(
                f'could not import world model {modelName}') from None
        else:
            raise
    finally:
        loadingModel = False
    names = module.__dict__.get('__all__', None)
    if names is not None:
        for name in names:
            namespace[name] = getattr(module, name)
    else:
        for name, value in module.__dict__.items():
            if not name.startswith('_'):
                namespace[name] = value
Пример #3
0
    def forParameters(params, globalParams):
        """Create an `ExternalSampler` given the sets of external and global parameters.

		The scenario may explicitly select an external sampler by assigning the global
		parameter ``externalSampler`` to a subclass of `ExternalSampler`. Otherwise, a
		`VerifaiSampler` is used by default.

		Args:
			params (tuple): Tuple listing each `ExternalParameter`.
			globalParams (dict): Dictionary of global parameters for the `Scenario`.
			  Note that the values of these parameters may be instances of `Distribution`!

		Returns:
			An `ExternalSampler` configured for the given parameters.
		"""
        if len(params) > 0:
            externalSampler = globalParams.get('externalSampler',
                                               VerifaiSampler)
            if not issubclass(externalSampler, ExternalSampler):
                raise InvalidScenarioError(
                    f'externalSampler type {externalSampler}'
                    ' not subclass of ExternalSampler')
            return externalSampler(params, globalParams)
        else:
            return None
Пример #4
0
def pruneContainment(scenario, verbosity):
    """Prune based on the requirement that individual Objects fit within their container.

    Specifically, if O is positioned uniformly in region B and has container C, then we
    can instead pick a position uniformly in their intersection. If we can also lower
    bound the radius of O, then we can first erode C by that distance.
    """
    for obj in scenario.objects:
        base = matchInRegion(obj.position)
        if base is None:  # match objects positioned uniformly in a Region
            continue
        if isinstance(base, regions.EmptyRegion):
            raise InvalidScenarioError(f'Object {obj} placed in empty region')
        basePoly = regions.toPolygon(base)
        if basePoly is None:  # to prune, the Region must be polygonal
            continue
        if basePoly.is_empty:
            raise InvalidScenarioError(f'Object {obj} placed in empty region')
        container = scenario.containerOfObject(obj)
        containerPoly = regions.toPolygon(container)
        if containerPoly is None:  # the object's container must also be polygonal
            return None
        minRadius, _ = supportInterval(obj.inradius)
        if minRadius is not None:  # if we can lower bound the radius, erode the container
            containerPoly = containerPoly.buffer(-minRadius)
        elif base is container:
            continue
        newBasePoly = basePoly & containerPoly  # restrict the base Region to the container
        if newBasePoly.is_empty:
            raise InvalidScenarioError(
                f'Object {obj} does not fit in container')
        if verbosity >= 1:
            if basePoly.area > 0:
                ratio = newBasePoly.area / basePoly.area
            else:
                ratio = newBasePoly.length / basePoly.length
            percent = 100 * (1.0 - ratio)
            print(
                f'    Region containment constraint pruned {percent:.1f}% of space.'
            )
        newBase = regions.regionFromShapelyObject(newBasePoly,
                                                  orientation=base.orientation)
        newPos = regions.Region.uniformPointIn(newBase)
        obj.position.conditionTo(newPos)
Пример #5
0
    def _toScenario(self, namespace):
        assert self._prepared

        if self._ego is None and self._compose is None:
            msg = 'did not specify ego object'
            modScenarios = namespace['_scenarios']
            if self._dummyNamespace and len(modScenarios) == 1:
                if modScenarios[0]._requiresArguments():
                    msg += (
                        '\n(Note: this Scenic file contains a modular scenario, but it\n'
                        'cannot be used as the top-level scenario since it requires\n'
                        'arguments; so the whole file is being used as a top-level\n'
                        'scenario and needs an ego object.)')
                else:
                    msg += (
                        '\n(Note: this Scenic file contains a modular scenario, but also\n'
                        'other code; so the whole file is being used as a top-level\n'
                        'scenario and needs an ego object.)')
            raise InvalidScenarioError(msg)

        # Extract workspace, if one is specified
        if 'workspace' in namespace:
            workspace = namespace['workspace']
            if not isinstance(workspace, Workspace):
                raise InvalidScenarioError(
                    f'workspace {workspace} is not a Workspace')
            if needsSampling(workspace):
                raise InvalidScenarioError('workspace must be a fixed region')
            if needsLazyEvaluation(workspace):
                raise InvalidScenarioError('workspace uses value undefined '
                                           'outside of object definition')
        else:
            workspace = None

        from scenic.core.scenarios import Scenario
        scenario = Scenario(workspace, self._simulatorFactory, self._objects,
                            self._ego, self._globalParameters,
                            self._externalParameters, self._requirements,
                            self._requirementDeps, self._monitors,
                            self._behaviorNamespaces,
                            self)  # TODO unify these!
        return scenario
Пример #6
0
def model(namespace, modelName):
	if lockedModel is not None:
		modelName = lockedModel
	try:
		module = importlib.import_module(modelName)
	except ModuleNotFoundError:
		raise InvalidScenarioError(f'could not import world model {modelName}') from None
	names = module.__dict__.get('__all__', None)
	if names is not None:
		for name in names:
			namespace[name] = getattr(module, name)
	else:
		for name, value in module.__dict__.items():
			if not name.startswith('_'):
				namespace[name] = value
Пример #7
0
    def compile(self, namespace, scenario, syntax=None):
        """Create a closure testing the requirement in the correct runtime state.

        While we're at it, determine whether the requirement implies any relations
        we can use for pruning, and gather all of its dependencies.
        """
        bindings, ego, line = self.bindings, self.egoObject, self.line
        condition = self.condition

        # Check whether requirement implies any relations used for pruning
        if self.ty.constrainsSampling and syntax:
            relations.inferRelationsFrom(syntax, bindings, ego, line)

        # Gather dependencies of the requirement
        deps = set()
        for value in bindings.values():
            if needsSampling(value):
                deps.add(value)
            if needsLazyEvaluation(value):
                raise InvalidScenarioError(
                    f'requirement on line {line} uses value {value}'
                    ' undefined outside of object definition')
        if ego is not None:
            assert isinstance(ego, Samplable)
            deps.add(ego)

        # Construct closure
        def closure(values):
            global evaluatingRequirement, currentScenario
            # rebind any names referring to sampled objects
            for name, value in bindings.items():
                if value in values:
                    namespace[name] = values[value]
            # rebind ego object, which can be referred to implicitly
            boundEgo = None if ego is None else values[ego]
            # evaluate requirement condition, reporting errors on the correct line
            import scenic.syntax.veneer as veneer
            with veneer.executeInRequirement(scenario, boundEgo):
                result = condition()
                assert not needsSampling(result)
                if needsLazyEvaluation(result):
                    raise InvalidScenarioError(
                        f'requirement on line {line} uses value'
                        ' undefined outside of object definition')
            return result

        return CompiledRequirement(self, closure, deps)
Пример #8
0
 def closure(values):
     global evaluatingRequirement, currentScenario
     # rebind any names referring to sampled objects
     for name, value in bindings.items():
         if value in values:
             namespace[name] = values[value]
     # rebind ego object, which can be referred to implicitly
     boundEgo = None if ego is None else values[ego]
     # evaluate requirement condition, reporting errors on the correct line
     import scenic.syntax.veneer as veneer
     with veneer.executeInRequirement(scenario, boundEgo):
         result = condition()
         assert not needsSampling(result)
         if needsLazyEvaluation(result):
             raise InvalidScenarioError(
                 f'requirement on line {line} uses value'
                 ' undefined outside of object definition')
     return result
Пример #9
0
def inferDistanceRelations(matcher, reqNode, ego, line):
    """Infer bounds on distances from a requirement."""
    distMatcher = lambda node: matcher.matchUnaryFunction('DistanceFrom', node)
    allBounds = matcher.matchBounds(reqNode, distMatcher)
    for target, bounds in allBounds:
        if not isinstance(target, Object):
            continue
        assert target is not ego
        if ego is None:
            raise InvalidScenarioError(
                'distance w.r.t. unassigned ego on line {line}')
        lower, upper = bounds
        if lower < 0:
            lower = 0
            if upper == float('inf'):
                continue  # skip trivial bounds
        rel = DistanceRelation(target, lower, upper)
        ego._relations.append(rel)
        conv = DistanceRelation(ego, lower, upper)
        target._relations.append(conv)
Пример #10
0
def inferRelativeHeadingRelations(matcher, reqNode, ego, line):
    """Infer bounds on relative headings from a requirement."""
    rhMatcher = lambda node: matcher.matchUnaryFunction(
        'RelativeHeading', node)
    allBounds = matcher.matchBounds(reqNode, rhMatcher)
    for target, bounds in allBounds:
        if not isinstance(target, Object):
            continue
        assert target is not ego
        if ego is None:
            raise InvalidScenarioError(
                'relative heading w.r.t. unassigned ego on line {line}')
        lower, upper = bounds
        if lower < -math.pi:
            lower = -math.pi
        if upper > math.pi:
            upper = math.pi
        if lower == -math.pi and upper == math.pi:
            continue  # skip trivial bounds
        rel = RelativeHeadingRelation(target, lower, upper)
        ego._relations.append(rel)
        conv = RelativeHeadingRelation(ego, -upper, -lower)
        target._relations.append(conv)
Пример #11
0
    def generate(self, maxIterations=2000, verbosity=0, feedback=None):
        """Sample a `Scene` from this scenario.

		Args:
			maxIterations (int): Maximum number of rejection sampling iterations.
			verbosity (int): Verbosity level.
			feedback (float): Feedback to pass to external samplers doing active sampling.
				See :mod:`scenic.core.external_params`.

		Returns:
			A pair with the sampled `Scene` and the number of iterations used.

		Raises:
			`RejectionException`: if no valid sample is found in **maxIterations** iterations.
		"""
        objects = self.objects

        # choose which custom requirements will be enforced for this sample
        activeReqs = [
            req for req in self.initialRequirements
            if random.random() <= req.prob
        ]

        # do rejection sampling until requirements are satisfied
        rejection = True
        iterations = 0
        while rejection is not None:
            if iterations > 0:  # rejected the last sample
                if verbosity >= 2:
                    print(
                        f'  Rejected sample {iterations} because of: {rejection}'
                    )
                if self.externalSampler is not None:
                    feedback = self.externalSampler.rejectionFeedback
            if iterations >= maxIterations:
                raise RejectionException(
                    f'failed to generate scenario in {iterations} iterations')
            iterations += 1
            try:
                if self.externalSampler is not None:
                    self.externalSampler.sample(feedback)
                sample = Samplable.sampleAll(self.dependencies)
            except RejectionException as e:
                rejection = e
                continue
            rejection = None
            ego = sample[self.egoObject]
            # Normalize types of some built-in properties
            for obj in objects:
                sampledObj = sample[obj]
                assert not needsSampling(sampledObj)
                # position, heading
                assert isinstance(sampledObj.position, Vector)
                sampledObj.heading = float(sampledObj.heading)
                # behavior
                behavior = sampledObj.behavior
                if behavior is not None and not isinstance(
                        behavior, veneer.Behavior):
                    raise InvalidScenarioError(
                        f'behavior {behavior} of Object {obj} is not a behavior'
                    )

            # Check built-in requirements
            for i in range(len(objects)):
                vi = sample[objects[i]]
                # Require object to be contained in the workspace/valid region
                container = self.containerOfObject(vi)
                if not container.containsObject(vi):
                    rejection = 'object containment'
                    break
                # Require object to be visible from the ego object
                if vi.requireVisible and vi is not ego and not ego.canSee(vi):
                    rejection = 'object visibility'
                    break
                # Require object to not intersect another object
                for j in range(i):
                    vj = sample[objects[j]]
                    if vi.intersects(vj):
                        rejection = 'object intersection'
                        break
                if rejection is not None:
                    break
            if rejection is not None:
                continue
            # Check user-specified requirements
            for req in activeReqs:
                if not req.satisfiedBy(sample):
                    rejection = f'user-specified requirement (line {req.line})'
                    break

        # obtained a valid sample; assemble a scene from it
        sampledObjects = tuple(sample[obj] for obj in objects)
        sampledParams = {}
        for param, value in self.params.items():
            sampledValue = sample[value]
            assert not needsLazyEvaluation(sampledValue)
            sampledParams[param] = sampledValue
        sampledNamespaces = {}
        for modName, namespace in self.behaviorNamespaces.items():
            sampledNamespace = {
                name: sample[value]
                for name, value in namespace.items()
            }
            sampledNamespaces[modName] = (namespace, sampledNamespace,
                                          namespace.copy())
        alwaysReqs = (veneer.BoundRequirement(req, sample)
                      for req in self.alwaysRequirements)
        terminationConds = (veneer.BoundRequirement(req, sample)
                            for req in self.terminationConditions)
        termSimulationConds = (veneer.BoundRequirement(req, sample)
                               for req in self.terminateSimulationConditions)
        scene = Scene(self.workspace, sampledObjects, ego, sampledParams,
                      alwaysReqs, terminationConds, termSimulationConds,
                      self.monitors, sampledNamespaces, self.dynamicScenario)
        return scene, iterations
Пример #12
0
    def run(self, maxSteps):
        """Run the simulation.

        Throws a RejectSimulationException if a requirement is violated.
        """
        trajectory = self.trajectory
        if self.currentTime > 0:
            raise RuntimeError('tried to run a Simulation which has already run')
        assert len(trajectory) == 1
        actionSequence = []

        import scenic.syntax.veneer as veneer
        veneer.beginSimulation(self)
        dynamicScenario = self.scene.dynamicScenario

        try:
            # Initialize dynamic scenario
            dynamicScenario._start()

            # Update all objects in case the simulator has adjusted any dynamic
            # properties during setup
            self.updateObjects()

            # Run simulation
            assert self.currentTime == 0
            terminationReason = None
            while maxSteps is None or self.currentTime < maxSteps:
                if self.verbosity >= 3:
                    print(f'    Time step {self.currentTime}:')

                # Run compose blocks of compositional scenarios
                terminationReason = dynamicScenario._step()

                # Check if any requirements fail
                dynamicScenario._checkAlwaysRequirements()

                # Run monitors
                newReason = dynamicScenario._runMonitors()
                if newReason is not None:
                    terminationReason = newReason

                # "Always" and scenario-level requirements have been checked;
                # now safe to terminate if the top-level scenario has finished
                # or a monitor requested termination
                if terminationReason is not None:
                    break
                terminationReason = dynamicScenario._checkSimulationTerminationConditions()
                if terminationReason is not None:
                    break

                # Compute the actions of the agents in this time step
                allActions = OrderedDict()
                schedule = self.scheduleForAgents()
                for agent in schedule:
                    behavior = agent.behavior
                    if not behavior._runningIterator:   # TODO remove hack
                        behavior.start(agent)
                    actions = behavior.step()
                    if isinstance(actions, EndSimulationAction):
                        terminationReason = str(actions)
                        break
                    assert isinstance(actions, tuple)
                    if len(actions) == 1 and isinstance(actions[0], (list, tuple)):
                        actions = tuple(actions[0])
                    if not self.actionsAreCompatible(agent, actions):
                        raise InvalidScenarioError(f'agent {agent} tried incompatible '
                                                   f' action(s) {actions}')
                    allActions[agent] = actions
                if terminationReason is not None:
                    break

                # Execute the actions
                if self.verbosity >= 3:
                    for agent, actions in allActions.items():
                        print(f'      Agent {agent} takes action(s) {actions}')
                self.executeActions(allActions)

                # Run the simulation for a single step and read its state back into Scenic
                self.step()
                self.updateObjects()
                self.currentTime += 1

                # Save the new state
                trajectory.append(self.currentState())
                actionSequence.append(allActions)

            if terminationReason is None:
                terminationReason = f'reached time limit ({maxSteps} steps)'
            result = SimulationResult(trajectory, actionSequence, terminationReason)
            return result
        finally:
            self.destroy()
            for obj in self.scene.objects:
                disableDynamicProxyFor(obj)
            for agent in self.agents:
                agent.behavior.stop()
            for monitor in self.scene.monitors:
                monitor.stop()
            veneer.endSimulation(self)
Пример #13
0
def _rejectSample(message):
    if veneer.isActive():
        raise InvalidScenarioError(message)
    else:
        raise RejectionException(message)