コード例 #1
0
def parse_expression(exp):
    if isinstance(exp, list):
        operator_or_functionsymbol = exp[0]
        if operator_or_functionsymbol in ("+", "/", "*", "-"):  # NFD
            # TODO: print warning if only :action-cost is used in PDDL requirements, but not :numeric-fluents
            args = [parse_expression(arg) for arg in exp[1:]]
            operator = operator_or_functionsymbol
        else:
            # TODO: if only :action-costs are used, operator_or_functionsymbol MUST be "total-cost"
            # TODO: Support terms. right now a numeric variable is expected here
            return pddl.PrimitiveNumericExpression(operator_or_functionsymbol,
                                                   exp[1:])
        if operator == "+":
            return pddl.Sum(args)
        elif operator == "/":
            assert len(args) == 2
            return pddl.Quotient(args)
        elif operator == "*":
            return pddl.Product(args)
        else:
            if len(args) == 1:
                return pddl.AdditiveInverse(args)
            else:
                assert len(args) == 2
                return pddl.Difference(args)
    elif isFloat(exp):
        # TODO: another place where a warning might be useful if only :action-costs but
        #   not :numeric-fluents are used. Negative numbers are only supported with :numeric-fluents
        return pddl.NumericConstant(float(exp))
    else:
        return pddl.PrimitiveNumericExpression(exp, [])
コード例 #2
0
def parse_expression(exp):
    if isinstance(exp, list):
        functionsymbol = exp[0]
        return pddl.PrimitiveNumericExpression(functionsymbol, exp[1:])
    elif exp.replace(".", "").isdigit():
        return pddl.NumericConstant(float(exp))
    elif exp[0] == "-":
        raise ValueError("Negative numbers are not supported")
    else:
        return pddl.PrimitiveNumericExpression(exp, [])
コード例 #3
0
ファイル: downward.py プロジェクト: nobodyczcz/pddlstream
def make_cost(cost):
    if cost is None:
        return cost
    fluent = pddl.PrimitiveNumericExpression(symbol=TOTAL_COST, args=[])
    try:
        expression = pddl.NumericConstant(cost)
    except TypeError:
        expression = pddl.PrimitiveNumericExpression(
            symbol=get_prefix(cost), args=list(map(pddl_from_object, get_args(cost))))
    return pddl.Increase(fluent=fluent, expression=expression)
コード例 #4
0
ファイル: simultaneous.py プロジェクト: jingxixu/pddlstream
def get_stream_action(result, name, unit_cost, effect_scale=1):
    #from pddl_parser.parsing_functions import parse_action
    import pddl

    parameters = []
    preconditions = [
        fd_from_fact(fact) for fact in result.instance.get_domain()
    ]
    precondition = pddl.Conjunction(preconditions)
    effects = [
        pddl.Effect(parameters=[],
                    condition=pddl.Truth(),
                    literal=fd_from_fact(fact))
        for fact in result.get_certified()
    ]

    effort = 1 if unit_cost else result.instance.get_effort()
    if effort == INF:
        return None
    fluent = pddl.PrimitiveNumericExpression(symbol=TOTAL_COST, args=[])
    expression = pddl.NumericConstant(int_ceil(effect_scale *
                                               effort))  # Integer
    cost = pddl.Increase(fluent=fluent,
                         expression=expression)  # Can also be None

    return pddl.Action(name=name,
                       parameters=parameters,
                       num_external_parameters=len(parameters),
                       precondition=precondition,
                       effects=effects,
                       cost=cost)
コード例 #5
0
ファイル: instantiate.py プロジェクト: speckdavid/shakey2016
def get_fluent_functions(model):
    fluent_pnes = set()
    for atom in model:
        if isinstance(atom.predicate, pddl.PrimitiveNumericExpression):
            fluent_pnes.add(
                pddl.PrimitiveNumericExpression(atom.predicate.symbol,
                                                atom.args))
    return fluent_pnes
コード例 #6
0
def get_fluent_functions(model):
    fluent_pnes = set()
    for atom in model:
        if isinstance(atom.predicate, pddl.PrimitiveNumericExpression):
            #             print("instantiate.get_fluent_functions adds atom" ,atom)
            #            assert(atom.predicate.ntype != 'U') , "Atom is falsch %s" % atom
            fluent_pnes.add(
                pddl.PrimitiveNumericExpression(atom.predicate.symbol,
                                                atom.args,
                                                atom.predicate.ntype))
    return fluent_pnes
コード例 #7
0
ファイル: exogenous.py プロジェクト: m1sk/pddlstream
def compile_to_exogenous_actions(evaluations, domain, streams):
    import pddl
    # TODO: automatically derive fluents
    # TODO: version of this that operates on fluents of length one?
    # TODO: better instantiation when have full parameters
    # TODO: conversion from stream cost to real cost units?
    # TODO: any predicates derived would need to be replaced as well
    fluent_predicates = get_fluents(domain)
    domain_predicates = {get_prefix(a) for s in streams for a in s.domain}
    if not (domain_predicates & fluent_predicates):
        return

    certified_predicates = {get_prefix(a) for s in streams for a in s.certified}
    future_map = {p: 'f-{}'.format(p) for p in certified_predicates}
    augment_evaluations(evaluations, future_map)
    rename_future = lambda a: rename_atom(a, future_map)
    for stream in list(streams):
        if not isinstance(stream, Stream):
            raise NotImplementedError(stream)
        # TODO: could also just have conditions asserting that one of the fluent conditions fails
        streams.append(create_static_stream(stream, evaluations, fluent_predicates, rename_future))
        stream_atom = streams[-1].certified[0]
        parameters = [pddl.TypedObject(p, 'object') for p in get_args(stream_atom)]
        # TODO: add to predicates as well?
        domain.predicate_dict[get_prefix(stream_atom)] = pddl.Predicate(get_prefix(stream_atom), parameters)
        precondition = pddl.Conjunction(tuple(map(fd_from_fact, (stream_atom,) + tuple(stream.domain))))
        effects = [pddl.Effect(parameters=[], condition=pddl.Truth(),
                               literal=fd_from_fact(fact)) for fact in stream.certified]
        effort = 1 # TODO: use stream info
        #effort = 1 if unit_cost else result.instance.get_effort()
        #if effort == INF:
        #    continue
        fluent = pddl.PrimitiveNumericExpression(symbol=TOTAL_COST, args=[])
        expression = pddl.NumericConstant(int_ceil(effort)) # Integer
        cost = pddl.Increase(fluent=fluent, expression=expression) # Can also be None
        domain.actions.append(pddl.Action(name='call-{}'.format(stream.name),
                                          parameters=parameters,
                                          num_external_parameters=len(parameters),
                                          precondition=precondition, effects=effects, cost=cost))
        stream.certified = tuple(set(stream.certified) |
                                 set(map(rename_future, stream.certified)))
コード例 #8
0
ファイル: sequential.py プロジェクト: yijiangh/pddlstream
def simplify_actions(opt_evaluations, action_plan, task, actions, unit_costs):
    # TODO: add ordering constraints to simplify the optimization
    import pddl
    import instantiate

    fluent_facts = MockSet()
    init_facts = set()
    type_to_objects = instantiate.get_objects_by_type(task.objects, task.types)
    results_from_head = get_results_from_head(opt_evaluations)

    action_from_name = {}
    function_plan = set()
    for i, (name, args) in enumerate(action_plan):
        action = find_unique(lambda a: a.name == name, actions)
        assert (len(action.parameters) == len(args))
        # parameters = action.parameters[:action.num_external_parameters]
        var_mapping = {p.name: a for p, a in zip(action.parameters, args)}
        new_name = '{}-{}'.format(name, i)
        new_parameters = action.parameters[len(args):]
        new_preconditions = []
        action.precondition.instantiate(var_mapping, init_facts, fluent_facts, new_preconditions)
        new_effects = []
        for eff in action.effects:
            eff.instantiate(var_mapping, init_facts, fluent_facts, type_to_objects, new_effects)
        new_effects = [pddl.Effect([], pddl.Conjunction(conditions), effect)
                       for conditions, effect in new_effects]
        cost = pddl.Increase(fluent=pddl.PrimitiveNumericExpression(symbol=TOTAL_COST, args=[]),
                             expression=pddl.NumericConstant(1))
        # cost = None
        task.actions.append(pddl.Action(new_name, new_parameters, len(new_parameters),
                                        pddl.Conjunction(new_preconditions), new_effects, cost))
        action_from_name[new_name] = (name, map(obj_from_pddl, args))
        if not unit_costs:
            function_result = extract_function_results(results_from_head, action, args)
            if function_result is not None:
                function_plan.add(function_result)
    return action_from_name, list(function_plan)
コード例 #9
0
def instantiate(task, model):
  relaxed_reachable = False
  fluent_facts = get_fluent_facts(task, model)
  fluent_functions = get_fluent_functions(model)

  ## HACK: This is a not very clean way of initializing the previously
  ## added functions that store the duration of an action to a haphazardly value
  for atom in model:
  	if isinstance(atom.predicate,str) and atom.predicate.startswith("defined!duration_"):
		pne = pddl.PrimitiveNumericExpression(atom.predicate.replace("defined!","",1),atom.args)
		value = pddl.NumericConstant(1.0)
		init_assign = pddl.Assign(pne, value)
		task.init.append(init_assign)

  init_facts = set(task.init) # TODO adapt
  init_function_vals = init_function_values(init_facts)

#  print "** fluent functions"
#  for function in fluent_functions:
#    function.dump()
#  print "** fluent facts"
#  for fact in fluent_facts:
#    print fact
#  print "** init facts"
#  for fact in init_facts:
#    print fact

  type_to_objects = get_objects_by_type(task.objects,task.types)

  instantiated_actions = []
  instantiated_durative_actions = []
  instantiated_axioms = []
  instantiated_numeric_axioms = set()
  new_constant_numeric_axioms = set()
  reachable_action_parameters = defaultdict(list)
  for atom in model:
    if isinstance(atom.predicate, pddl.Action):
      action = atom.predicate
      parameters = action.parameters
      if isinstance(action.condition, pddl.ExistentialCondition):
        parameters = list(parameters)
        parameters += action.condition.parameters
      variable_mapping = dict([(pddl.Variable(par.name), arg)
                               for par, arg in zip(parameters, atom.args)])
      inst_action = action.instantiate(variable_mapping, init_facts,
                                       fluent_facts, init_function_vals, fluent_functions,
                                       task, new_constant_numeric_axioms, type_to_objects)
      if inst_action:
        instantiated_actions.append(inst_action)
    elif isinstance(atom.predicate, pddl.DurativeAction):
      action = atom.predicate
      parameters = action.parameters
      reachable_action_parameters[action.name].append(parameters)
      for condition in action.condition:
        if isinstance(condition,pddl.ExistentialCondition):
          parameters = list(parameters)
          parameters += condition.parameters
      variable_mapping = dict([(pddl.Variable(par.name), arg)
                               for par, arg in zip(parameters, atom.args)])
      inst_action = action.instantiate(variable_mapping, init_facts, fluent_facts,
                                       init_function_vals, fluent_functions,
                                       task, new_constant_numeric_axioms, type_to_objects)
      if inst_action:
        instantiated_durative_actions.append(inst_action)
    elif isinstance(atom.predicate, pddl.Axiom):
      axiom = atom.predicate
      parameters = axiom.parameters
      if isinstance(axiom.condition, pddl.ExistentialCondition):
        parameters = list(parameters)
        parameters += axiom.condition.parameters
      variable_mapping = dict([(pddl.Variable(par.name), arg)
                               for par, arg in zip(parameters, atom.args)])
      inst_axiom = axiom.instantiate(variable_mapping, init_facts, fluent_facts,
                                     fluent_functions, init_function_vals, task,
                                     new_constant_numeric_axioms)
      if inst_axiom:
        instantiated_axioms.append(inst_axiom)
    elif isinstance(atom.predicate, pddl.NumericAxiom):
      axiom = atom.predicate
      variable_mapping = dict([(pddl.Variable(par.name), arg)
                               for par, arg in zip(axiom.parameters, atom.args)])
      new_constant_numeric_axioms = set()
      inst_axiom = axiom.instantiate(variable_mapping, fluent_functions, init_function_vals, 
                                     task, new_constant_numeric_axioms)
      instantiated_numeric_axioms.add(inst_axiom)
    elif atom.predicate == "@goal-reachable":
      relaxed_reachable = True
    instantiated_numeric_axioms |= new_constant_numeric_axioms
      
  return (relaxed_reachable, fluent_facts, fluent_functions, instantiated_actions, 
          instantiated_durative_actions, instantiated_axioms,
          instantiated_numeric_axioms, reachable_action_parameters)
コード例 #10
0
ファイル: sequential.py プロジェクト: m1sk/pddlstream
def sequential_stream_plan(evaluations, goal_expression, domain, stream_results, negated, unit_costs=True, **kwargs):
    if negated:
        raise NotImplementedError()
    # TODO: compute preimage and make that the goal instead
    opt_evaluations = evaluations_from_stream_plan(evaluations, stream_results)
    opt_task = task_from_domain_problem(domain, get_problem(opt_evaluations, goal_expression, domain, unit_costs))
    action_plan, action_cost = solve_from_task(opt_task, **kwargs)
    if action_plan is None:
        return None, action_cost

    import instantiate
    fluent_facts = MockSet()
    init_facts = set()
    task = task_from_domain_problem(domain, get_problem(evaluations, goal_expression, domain, unit_costs))

    type_to_objects = instantiate.get_objects_by_type(task.objects, task.types)
    task.actions, stream_result_from_name = get_stream_actions(stream_results)
    results_from_head = get_results_from_head(opt_evaluations)

    # TODO: add ordering constraints to simplify the optimization
    import pddl
    action_from_name = {}
    function_plan = set()
    for i, (name, args) in enumerate(action_plan):
        action = find_unique(lambda a: a.name == name, domain.actions)
        assert(len(action.parameters) == len(args))
        #parameters = action.parameters[:action.num_external_parameters]
        var_mapping = {p.name: a for p, a in zip(action.parameters, args)}
        new_name = '{}-{}'.format(name, i)
        new_parameters = action.parameters[len(args):]
        new_preconditions = []
        action.precondition.instantiate(var_mapping, init_facts, fluent_facts, new_preconditions)
        new_effects = []
        for eff in action.effects:
            eff.instantiate(var_mapping, init_facts, fluent_facts, type_to_objects, new_effects)
        new_effects = [pddl.Effect([], pddl.Conjunction(conditions), effect)
                      for conditions, effect in new_effects]
        cost = pddl.Increase(fluent=pddl.PrimitiveNumericExpression(symbol=TOTAL_COST, args=[]),
                             expression=pddl.NumericConstant(1))
        #cost = None
        task.actions.append(pddl.Action(new_name, new_parameters, 0,
                                   pddl.Conjunction(new_preconditions), new_effects, cost))
        action_from_name[new_name] = (name, map(obj_from_pddl, args))
        if not unit_costs:
            function_plan.update(extract_function_results(results_from_head, action, args))

    planner = kwargs.get('planner', 'ff-astar')
    combined_plan, _ = solve_from_task(task, planner=planner, **kwargs)
    if combined_plan is None:
        return None, obj_from_pddl_plan(action_plan), INF
    stream_plan = []
    action_plan = []
    for name, args in combined_plan:
        if name in stream_result_from_name:
            stream_plan.append(stream_result_from_name[name])
        else:
            action_plan.append(action_from_name[name])
    stream_plan += list(function_plan)
    combined_plan = stream_plan + action_plan

    return combined_plan, action_cost
コード例 #11
0
def parse_task_pddl(task_pddl, type_dict, predicate_dict):
    iterator = iter(task_pddl)

    define_tag = next(iterator)
    assert define_tag == "define"
    problem_line = next(iterator)
    assert problem_line[0] == "problem" and len(problem_line) == 2
    yield problem_line[1]
    domain_line = next(iterator)
    assert domain_line[0] == ":domain" and len(domain_line) == 2
    yield domain_line[1]

    requirements_opt = next(iterator)
    if requirements_opt[0] == ":requirements":
        requirements = requirements_opt[1:]
        objects_opt = next(iterator)
    else:
        requirements = []
        objects_opt = requirements_opt
    yield pddl.Requirements(requirements)

    if objects_opt[0] == ":objects":
        yield parse_typed_list(objects_opt[1:])
        init = next(iterator)
    else:
        yield []
        init = objects_opt

    assert init[0] == ":init"
    initial = []
    num_initial = []
    initial_true = set()
    initial_false = set()
    initial_assignments = dict()
    for fact in init[1:]:
        #        if DEBUG: print("Analyzing fact %s"%fact)
        if fact[0] == "=":
            #            if DEBUG: print("Fact is '=' assignment")
            try:
                assignment = parse_assignment(fact)
            except ValueError as e:
                raise SystemExit("Error in initial state specification\n" +
                                 "Reason: %s." % e)
            if not isinstance(assignment.expression, pddl.NumericConstant):
                raise SystemExit("Illegal assignment in initial state " +
                                 "specification:\n%s" % assignment)
            if assignment.fluent in initial_assignments:
                prev = initial_assignments[assignment.fluent]
                if assignment.expression == prev.expression:
                    print("Warning: %s is specified twice" % assignment,
                          "in initial state specification")
                else:
                    raise SystemExit("Error in initial state specification\n" +
                                     "Reason: conflicting assignment for " +
                                     "%s." % assignment.fluent)
            else:
                initial_assignments[assignment.fluent] = assignment
                num_initial.append(assignment)
        elif fact[0] == "not":
            #            if DEBUG: print("Fact is negation")
            atom = pddl.Atom(fact[1][0], fact[1][1:])
            check_atom_consistency(atom, initial_false, initial_true, False)
            initial_false.add(atom)
        else:
            #            if DEBUG: print("Fact is positive atom")
            atom = pddl.Atom(fact[0], fact[1:])
            check_atom_consistency(atom, initial_true, initial_false)
            initial_true.add(atom)
#    if DEBUG: print("initial_true is %s"%sorted(initial_true))
    initial.extend(initial_true)
    if DEBUG:
        initial.sort(
            key=lambda x: x.__repr__())  # makes the result deterministic
    #    print("initial is s%s"%initial)
    yield initial
    # yielding num_initial delayed (metric section may add artificial fluent 'total-cost'

    # goal
    goal = next(iterator)
    assert goal[0] == ":goal" and len(goal) == 2

    #metric
    metric_symbol = '<'  # minimize
    metric_expression = None  # unit cost
    for entry in iterator:
        if entry[0] == ":metric":
            if entry[1] == "minimize":
                metric_symbol = '<'
            else:
                assert entry[
                    1] == "maximize", "Unknown metric, 'minimize' or 'maximize' expected."
                metric_symbol = '>'
#            try:
            metric_expression = parse_expression(entry[2])
#            except:
#                raise SystemExit("Cannot parse metric expression in %s" % entry[2])
    if metric_expression is None:
        metric_expression = pddl.PrimitiveNumericExpression(
            'total-cost', [], 'I')
        num_initial.append(parse_assignment(['=', 'total-cost', 0]))


#    print("parsing_functions yields num_initial:", num_initial)
    yield num_initial
    yield parse_condition(goal[1], type_dict, predicate_dict)
    yield (metric_symbol, metric_expression)

    for entry in iterator:
        assert False, entry
コード例 #12
0
ファイル: instantiate.py プロジェクト: speckdavid/shakey2016
def instantiate(task, model):
    relaxed_reachable = False
    fluent_facts = get_fluent_facts(task, model)
    fluent_functions = get_fluent_functions(model)

    ## HACK: This is a not very clean way of initializing the previously
    ## added functions that store the duration of an action to a haphazardly value
    for atom in model:
        if isinstance(atom.predicate,
                      str) and atom.predicate.startswith("defined!duration_"):
            pne = pddl.PrimitiveNumericExpression(
                atom.predicate.replace("defined!", "", 1), atom.args)
            value = pddl.NumericConstant(1.0)
            init_assign = pddl.Assign(pne, value)
            task.init.append(init_assign)

    init_facts = set(task.init)  # TODO adapt
    init_function_vals = init_function_values(init_facts)

    # Determine initial facts, that are not fluents => constant facts, that a module might need
    init_constant_fluents = set(init_function_vals)
    init_constant_fluents.difference_update(
        fluent_functions
    )  # all fluents that are in init, but are NOT a fluent -> constant

    # Now get the assigned values from the init_facts for the constant fluents
    init_constant_numeric_facts = set(
    )  # This will hold Assigns that assign the fluents
    for i in init_constant_fluents:
        for j in init_facts:
            if isinstance(j, pddl.Assign):
                if isinstance(j.fluent, pddl.PrimitiveNumericExpression):
                    if j.fluent is i:  # Assign in init_fact assign this (i) fluent
                        init_constant_numeric_facts.add(j)

    # Now get predicates that are in init, but are not fluent_facts
    init_constant_predicate_facts = set()
    for i in init_facts:
        if isinstance(i, pddl.Atom):  # do NOT consider PNEs, etc.
            if i not in fluent_facts:  # only consider non-fluents
                if i.predicate is not "=":  # hack to remove the intermediate '=' fluents
                    init_constant_predicate_facts.add(i)


#  print "** fluent functions"
#  for function in fluent_functions:
#    function.dump()
#  print "** fluent facts"
#  for fact in fluent_facts:
#    print fact
#  print "** init facts"
#  for fact in init_facts:
#    print fact

    type_to_objects = get_objects_by_type(task.objects, task.types)

    instantiated_actions = []
    instantiated_durative_actions = []
    instantiated_axioms = []
    instantiated_numeric_axioms = set()
    new_constant_numeric_axioms = set()
    reachable_action_parameters = defaultdict(list)
    instantiated_modules = set()
    for atom in model:
        if isinstance(atom.predicate, pddl.Action):
            action = atom.predicate
            parameters = action.parameters
            if isinstance(action.condition, pddl.ExistentialCondition):
                parameters = list(parameters)
                parameters += action.condition.parameters
            variable_mapping = dict([
                (pddl.Variable(par.name), arg)
                for par, arg in zip(parameters, atom.args)
            ])
            inst_action = action.instantiate(variable_mapping, init_facts,
                                             fluent_facts, init_function_vals,
                                             fluent_functions, task,
                                             new_constant_numeric_axioms,
                                             instantiated_modules,
                                             type_to_objects)
            if inst_action:
                instantiated_actions.append(inst_action)
        elif isinstance(atom.predicate, pddl.DurativeAction):
            action = atom.predicate
            parameters = action.parameters
            reachable_action_parameters[action.name].append(parameters)
            for condition in action.condition:
                if isinstance(condition, pddl.ExistentialCondition):
                    parameters = list(parameters)
                    parameters += condition.parameters
            variable_mapping = dict([
                (pddl.Variable(par.name), arg)
                for par, arg in zip(parameters, atom.args)
            ])
            inst_action = action.instantiate(variable_mapping, init_facts,
                                             fluent_facts, init_function_vals,
                                             fluent_functions, task,
                                             new_constant_numeric_axioms,
                                             instantiated_modules,
                                             type_to_objects)
            if inst_action:
                instantiated_durative_actions.append(inst_action)
        elif isinstance(atom.predicate, pddl.Axiom):
            axiom = atom.predicate
            parameters = axiom.parameters
            if isinstance(axiom.condition, pddl.ExistentialCondition):
                parameters = list(parameters)
                parameters += axiom.condition.parameters
            variable_mapping = dict([
                (pddl.Variable(par.name), arg)
                for par, arg in zip(parameters, atom.args)
            ])
            inst_axiom = axiom.instantiate(variable_mapping, init_facts,
                                           fluent_facts, fluent_functions,
                                           init_function_vals, task,
                                           new_constant_numeric_axioms,
                                           instantiated_modules)
            if inst_axiom:
                instantiated_axioms.append(inst_axiom)
        elif isinstance(atom.predicate, pddl.NumericAxiom):
            axiom = atom.predicate
            variable_mapping = dict([
                (pddl.Variable(par.name), arg)
                for par, arg in zip(axiom.parameters, atom.args)
            ])
            new_constant_numeric_axioms = set()
            inst_axiom = axiom.instantiate(variable_mapping, fluent_functions,
                                           init_function_vals, task,
                                           new_constant_numeric_axioms)
            instantiated_numeric_axioms.add(inst_axiom)
        elif atom.predicate == "@goal-reachable":
            relaxed_reachable = True
        instantiated_numeric_axioms |= new_constant_numeric_axioms

    return (relaxed_reachable, fluent_facts, fluent_functions,
            instantiated_actions, instantiated_durative_actions,
            instantiated_axioms, instantiated_numeric_axioms,
            instantiated_modules, init_constant_predicate_facts,
            init_constant_numeric_facts, reachable_action_parameters)
コード例 #13
0
def make_cost(cost):
    fluent = pddl.PrimitiveNumericExpression(symbol=TOTAL_COST, args=[])
    expression = pddl.NumericConstant(cost)
    return pddl.Increase(fluent=fluent, expression=expression)