配置

Pyomo 配置系统

Pyomo 配置系统提供了三个类 (ConfigDict, ConfigList, 和 ConfigValue) 用于管理和记录结构化 配置信息和用户输入。该系统基于 ConfigValue 类,该类为单个配置 条目提供存储。ConfigValue 对象可以使用两个容器 (ConfigDict 和 ConfigList)进行分组,它们分别提供类似于 Python 的 dict 和 list 类的功能。

在最简单的情况下,Config系统允许开发者指定一个包含文档化配置项的字典:

from pyomo.common.config import (
    ConfigDict, ConfigList, ConfigValue
)
config = ConfigDict()
config.declare('filename', ConfigValue(
    default=None,
    domain=str,
    description="Input file name",
))
config.declare("bound tolerance", ConfigValue(
    default=1E-5,
    domain=float,
    description="Bound tolerance",
    doc="Relative tolerance for bound feasibility checks"
))
config.declare("iteration limit", ConfigValue(
    default=30,
    domain=int,
    description="Iteration limit",
    doc="Number of maximum iterations in the decomposition methods"
))

用户可以为这些条目提供值,并检索当前值:

>>> config['filename'] = 'tmp.txt'
>>> print(config['filename'])
tmp.txt
>>> print(config['iteration limit'])
30

为了方便,ConfigDict 对象支持通过属性进行读写访问(声明名称中的空格用下划线替换):

>>> print(config.filename)
tmp.txt
>>> print(config.iteration_limit)
30
>>> config.iteration_limit = 20
>>> print(config.iteration_limit)
20

域名验证

所有Config对象都支持一个domain关键字,该关键字接受一个可调用对象(类型、函数或可调用实例)。domain可调用对象应接收数据并将其映射到所需的域,可选地执行域验证(有关更多信息,请参见ConfigValueConfigDictConfigList)。这使得客户端代码可以接受非常灵活的输入集,而不会因输入验证而“混乱”代码:

>>> config.iteration_limit = 35.5
>>> print(config.iteration_limit)
35
>>> print(type(config.iteration_limit).__name__)
int

除了常见类型(如intfloatboolstr)之外,配置系统还提供了许多用于常见用例的自定义领域验证器:

Bool(val)

用于布尔类型对象的域验证器。

Integer(val)

允许整数的域验证函数

PositiveInt(val)

域验证函数,仅接受严格的正整数

NegativeInt(val)

域验证函数,允许严格负整数

NonNegativeInt(val)

域验证函数允许整数 >= 0

NonPositiveInt(val)

域验证函数允许整数 <= 0

PositiveFloat(val)

域验证函数,仅接受严格正数

NegativeFloat(val)

域验证函数允许严格负数

NonPositiveFloat(val)

域验证函数允许小于或等于0的数字

NonNegativeFloat(val)

域验证函数,允许大于或等于0的数字

In(domain[, cast])

域验证类,允许包含可能值的容器

InEnum(domain)

域验证类,允许枚举值/名称。

IsInstance(*bases[, document_full_base_names])

用于类型检查的域验证器。

ListOf(itemtype[, domain, string_lexer])

指定类型列表的域验证器

Module([basePath, expandPath])

模块的域验证器。

Path([basePath, expandPath])

用于path-like object的域验证器。

PathList([basePath, expandPath])

用于验证一系列路径类对象的域验证器。

DynamicImplicitDomain(callback)

可以根据键返回自定义域的隐式域。

配置类层次结构

Config系统的一个特点是核心类都实现了__call__,并且它们本身可以用作domain值。除了为复杂的层次结构提供域验证外,此功能还允许ConfigDicts清晰地支持派生对象的配置。考虑以下示例:

>>> class Base(object):
...     CONFIG = ConfigDict()
...     CONFIG.declare('filename', ConfigValue(
...         default='input.txt',
...         domain=str,
...     ))
...     def __init__(self, **kwds):
...         c = self.CONFIG(kwds)
...         c.display()
...
>>> class Derived(Base):
...     CONFIG = Base.CONFIG()
...     CONFIG.declare('pattern', ConfigValue(
...         default=None,
...         domain=str,
...     ))
...
>>> tmp = Base(filename='foo.txt')
filename: foo.txt
>>> tmp = Derived(pattern='.*warning')
filename: input.txt
pattern: .*warning

在这里,基类 Base 声明了一个类级别的属性 CONFIG,它是一个包含单个条目 (filename) 的 ConfigDict。派生类 (Derived) 首先复制基类的 CONFIG,然后定义了一个额外的条目 (pattern)。基类的实例仍然会创建仅包含单个 filename 条目的 c 实例,而派生类的实例将具有包含两个条目的 c 实例:派生类声明的 pattern 条目,以及从基类“继承”的 filename 条目。

这种设计模式的扩展提供了一种处理“临时”实例选项的清晰方法。考虑一个与外部“求解器”的接口。我们的类实现了一个solve()方法,该方法接收一个问题并将其与一些求解器配置选项一起发送给求解器。我们希望能够在接口类的实例上“持久”设置这些选项,但仍然为每次调用solve()“临时”覆盖它们。我们通过为特定实例和每次solve()调用创建类的配置副本来实现这一点:

>>> class Solver(object):
...     CONFIG = ConfigDict()
...     CONFIG.declare('iterlim', ConfigValue(
...         default=10,
...         domain=int,
...     ))
...     def __init__(self, **kwds):
...         self.config = self.CONFIG(kwds)
...     def solve(self, model, **options):
...         config = self.config(options)
...         # Solve the model with the specified iterlim
...         config.display()
...
>>> solver = Solver()
>>> solver.solve(None)
iterlim: 10
>>> solver.config.iterlim = 20
>>> solver.solve(None)
iterlim: 20
>>> solver.solve(None, iterlim=50)
iterlim: 50
>>> solver.solve(None)
iterlim: 20

与argparse交互

除了基本的存储和检索功能外,Config系统还提供了与argparse命令行参数解析系统的钩子。可以使用declare_as_argument()方法将单个Config条目声明为argparse参数。为了使声明更简单,declare()方法返回已声明的Config对象,以便可以内联完成参数声明:

import argparse
config = ConfigDict()
config.declare('iterlim', ConfigValue(
    domain=int,
    default=100,
    description="iteration limit",
)).declare_as_argument()
config.declare('lbfgs', ConfigValue(
    domain=bool,
    description="use limited memory BFGS update",
)).declare_as_argument()
config.declare('linesearch', ConfigValue(
    domain=bool,
    default=True,
    description="use line search",
)).declare_as_argument()
config.declare('relative tolerance', ConfigValue(
    domain=float,
    description="relative convergence tolerance",
)).declare_as_argument('--reltol', '-r', group='Tolerances')
config.declare('absolute tolerance', ConfigValue(
    domain=float,
    description="absolute convergence tolerance",
)).declare_as_argument('--abstol', '-a', group='Tolerances')

然后可以使用ConfigDict来初始化(或增强)一个argparse ArgumentParser对象:

parser = argparse.ArgumentParser("tester")
config.initialize_argparse(parser)

ConfigDict中的关键信息会自动转移到ArgumentParser对象中:

>>> print(parser.format_help())
usage: tester [-h] [--iterlim INT] [--lbfgs] [--disable-linesearch]
              [--reltol FLOAT] [--abstol FLOAT]
...
  -h, --help            show this help message and exit
  --iterlim INT         iteration limit
  --lbfgs               use limited memory BFGS update
  --disable-linesearch  [DON'T] use line search

Tolerances:
  --reltol... -r FLOAT  relative convergence tolerance
  --abstol... -a FLOAT  absolute convergence tolerance

解析后的参数可以重新导入到 ConfigDict 中:

>>> args=parser.parse_args(['--lbfgs', '--reltol', '0.1', '-a', '0.2'])
>>> args = config.import_argparse(args)
>>> config.display()
iterlim: 100
lbfgs: true
linesearch: true
relative tolerance: 0.1
absolute tolerance: 0.2

访问用户指定的值

了解用户明确设置了哪些值,以及用户明确设置但从未检索过的值,通常非常有用。配置系统提供了两个生成器方法来返回用户明确设置的项目(user_values())和设置但从未检索过的项目(unused_user_values()):

>>> print([val.name() for val in config.user_values()])
['lbfgs', 'relative tolerance', 'absolute tolerance']
>>> print(config.relative_tolerance)
0.1
>>> print([val.name() for val in config.unused_user_values()])
['lbfgs', 'absolute tolerance']

生成输出和文档

配置对象支持三种生成输出和文档的方法:display()generate_yaml_template()generate_documentation()。最简单的是 display(),它会打印出配置对象的当前值(如果它是容器类型,还会打印出所有子对象的值)。 generate_yaml_template() 类似于 display(),但它还包括描述字段作为格式化注释。

solver_config = config
config = ConfigDict()
config.declare('output', ConfigValue(
    default='results.yml',
    domain=str,
    description='output results filename'
))
config.declare('verbose', ConfigValue(
    default=0,
    domain=int,
    description='output verbosity',
    doc='This sets the system verbosity.  The default (0) only logs '
    'warnings and errors.  Larger integer values will produce '
    'additional log messages.',
))
config.declare('solvers', ConfigList(
    domain=solver_config,
    description='list of solvers to apply',
))
>>> config.display()
output: results.yml
verbose: 0
solvers: []
>>> print(config.generate_yaml_template())
output: results.yml  # output results filename
verbose: 0           # output verbosity
solvers: []          # list of solvers to apply

需要注意的是,这两种方法都记录了配置对象的当前状态。因此,在上面的例子中,由于solvers列表为空,你将不会获得列表中元素的任何信息。当然,如果你向列表中添加一个值,那么数据将会被输出:

>>> tmp = config()
>>> tmp.solvers.append({})
>>> tmp.display()
output: results.yml
verbose: 0
solvers:
  -
    iterlim: 100
    lbfgs: true
    linesearch: true
    relative tolerance: 0.1
    absolute tolerance: 0.2
>>> print(tmp.generate_yaml_template())
output: results.yml          # output results filename
verbose: 0                   # output verbosity
solvers:                     # list of solvers to apply
  -
    iterlim: 100             # iteration limit
    lbfgs: true              # use limited memory BFGS update
    linesearch: true         # use line search
    relative tolerance: 0.1  # relative convergence tolerance
    absolute tolerance: 0.2  # absolute convergence tolerance

第三种方法(generate_documentation())的行为有所不同。该方法旨在生成参考文档。对于每个配置项,输出doc字段。如果该项没有doc,则使用description字段。

列表容器有其记录,而不是当前值。文档可以通过可选参数进行配置。默认情况下生成LaTeX文档:

>>> print(config.generate_documentation())
\begin{description}[topsep=0pt,parsep=0.5em,itemsep=-0.4em]
  \item[{output}]\hfill
    \\output results filename
  \item[{verbose}]\hfill
    \\This sets the system verbosity.  The default (0) only logs warnings and
    errors.  Larger integer values will produce additional log messages.
  \item[{solvers}]\hfill
    \\list of solvers to apply
  \begin{description}[topsep=0pt,parsep=0.5em,itemsep=-0.4em]
    \item[{iterlim}]\hfill
      \\iteration limit
    \item[{lbfgs}]\hfill
      \\use limited memory BFGS update
    \item[{linesearch}]\hfill
      \\use line search
    \item[{relative tolerance}]\hfill
      \\relative convergence tolerance
    \item[{absolute tolerance}]\hfill
      \\absolute convergence tolerance
  \end{description}
\end{description}

枚举

ConfigEnum(值)

已弃用。

Classes

ConfigBase([默认值, 域, 描述, ...])

ConfigBlock

ConfigDict 的别名

ConfigDict([描述, 文档, 隐式, ...])

存储和操作配置值的字典。

ConfigFormatter()

ConfigList([默认值, 域, 描述, ...])

存储和操作配置值的列表。

ConfigValue([默认值, 域, 描述, ...])

存储和操作单个配置值。

DynamicImplicitDomain(callback)

可以根据键返回自定义域的隐式域。

ImmutableConfigValue(*args, **kwds)

In(domain[, cast])

域验证类,允许包含可能值的容器

InEnum(domain)

域验证类,允许枚举值/名称。

IsInstance(*bases[, document_full_base_names])

用于类型检查的域验证器。

LaTeX_ConfigFormatter()

ListOf(itemtype[, domain, string_lexer])

指定类型列表的域验证器

MarkImmutable(*args)

将ConfigValue的实例标记为不可变。

Module([basePath, expandPath])

模块的域验证器。

Path([basePath, expandPath])

用于path-like object的域验证器。

PathList([basePath, expandPath])

用于验证一系列路径类对象的域验证器。

String_ConfigFormatter(block_start, ...)

UninitializedMixin()

支持延迟数据初始化的Mixin类。

document_kwargs_from_configdict(config[, ...])

装饰器用于将ConfigDict的文档附加到docstring中

numpydoc_ConfigFormatter()

Functions

Bool(val)

用于布尔类型对象的域验证器。

Integer(val)

允许整数的域验证函数

NegativeFloat(val)

域验证函数允许严格负数

NegativeInt(val)

域验证函数,允许严格负整数

NonNegativeFloat(val)

域验证函数,允许大于或等于0的数字

NonNegativeInt(val)

域验证函数允许整数 >= 0

NonPositiveFloat(val)

域验证函数允许小于或等于0的数字

NonPositiveInt(val)

域验证函数允许整数 <= 0

PositiveFloat(val)

域验证函数,仅接受严格正数

PositiveInt(val)

域验证函数,仅接受严格的正整数

add_docstring_list(docstring, configdict[, ...])

已弃用。