Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/283.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 约束传播-如何处理一对多方向的三元约束_Python_Constraint Programming - Fatal编程技术网

Python 约束传播-如何处理一对多方向的三元约束

Python 约束传播-如何处理一对多方向的三元约束,python,constraint-programming,Python,Constraint Programming,我在以下代码中实现了一个框架(本书第二版第3.3.5节): import abc import numpy as np class Connector: """An object to store observable values.""" def __init__(self): self._value = None self._informant = None self._constraints = [] def

我在以下代码中实现了一个框架(本书第二版第3.3.5节):

import abc

import numpy as np


class Connector:
    """An object to store observable values."""

    def __init__(self):
        self._value = None
        self._informant = None
        self._constraints = []

    def has_value(self,):
        """Return true if connector has a value."""
        return self._informant is not None

    def get_value(self,):
        """Return connector value."""
        return self._value

    def set_value(self, new_value, informant='-USER-'):
        """Set value if none exists and inform constraints."""
        if not self.has_value():
            self._value = new_value
            self._informant = informant
            for constraint in reversed(self._constraints):
                if constraint != self._informant:
                    constraint.new_value()
        elif not np.array_equal(new_value, self._value):
            # Note: np needed as != is ambiguous on arrays.
            raise Contradiction("{} vs. {}".format(self._value, new_value))

    def forget_value(self, retractor='-USER-'):
        """Forget value and inform constraints."""
        if retractor == self._informant:
            self._informant = None
            for constraint in reversed(self._constraints):
                if constraint != retractor:
                    constraint.forget_value()

    def connect(self, new_constraint):
        """Add a new constraint."""
        if new_constraint not in self._constraints:
            self._constraints.append(new_constraint)
        if self.has_value():
            new_constraint.new_value()


class Constraint(metaclass=abc.ABCMeta):
    """ABC for constraints among connectors."""

    @abc.abstractmethod
    def new_value(self,):
        """Notify constraint of a new connector value."""
        pass

    @abc.abstractmethod
    def forget_value(self,):
        """Notify constraint to forget all observed connector values."""
        pass


class Adder(Constraint):
    """Defines the constraint x + y == z"""

    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z
        self.x.connect(self)
        self.y.connect(self)
        self.z.connect(self)

    def new_value(self,):
        if self.x.has_value() and self.y.has_value():
            self.z.set_value(self.x.get_value() + self.y.get_value(), self)
        elif self.x.has_value() and self.z.has_value():
            self.y.set_value(self.z.get_value() - self.x.get_value(), self)
        elif self.y.has_value() and self.z.has_value():
            self.x.set_value(self.z.get_value() - self.y.get_value(), self)

    def forget_value(self,):
        self.z.forget_value(self)
        self.x.forget_value(self)
        self.y.forget_value(self)
        self.new_value()


class Multiplier(Constraint):
    """Defines the constraint x * y == z."""

    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z
        self.x.connect(self)
        self.y.connect(self)
        self.z.connect(self)

    def new_value(self,):
        if self.x.has_value() and self.y.has_value():
            self.z.set_value(self.x.get_value() * self.y.get_value(), self)
        elif self.x.has_value() and self.z.has_value():
            self.y.set_value(self.z.get_value() / self.x.get_value(), self)
        elif self.y.has_value() and self.z.has_value():
            self.x.set_value(self.z.get_value() / self.y.get_value(), self)

    def forget_value(self,):
        self.z.forget_value(self)
        self.x.forget_value(self)
        self.y.forget_value(self)
        self.new_value()


class Power(Constraint):
    """Defines the constraint x ** y == z."""

    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z
        self.x.connect(self)
        self.y.connect(self)
        self.z.connect(self)

    def new_value(self,):
        if self.x.has_value() and self.y.has_value():
            self.z.set_value(self.x.get_value() ** self.y.get_value(), self)
        elif self.x.has_value() and self.z.has_value():
            self.y.set_value(np.log(self.z.get_value()) / np.log(self.x.get_value()), self)
        elif self.y.has_value() and self.z.has_value():
            self.x.set_value(self.z.get_value() ** (1.0 / self.y.get_value()), self)

    def forget_value(self,):
        self.z.forget_value(self)
        self.x.forget_value(self)
        self.y.forget_value(self)
        self.new_value()


class Equality(Constraint):
    """Defines the constraint x == y."""

    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.x.connect(self)
        self.y.connect(self)

    def new_value(self,):
        if self.x.has_value():
            self.y.set_value(self.x.get_value(), self)
        elif self.y.has_value():
            self.x.set_value(self.y.get_value(), self)

    def forget_value(self,):
        self.y.forget_value(self)
        self.x.forget_value(self)
        self.new_value()


class Constant(Constraint):
    """Defines the constraint x == value."""

    def __init__(self, x, value):
        x.connect(self)
        x.set_value(value, self)

    def new_value(self):
        raise Exception()

    def forget_value(self):
        raise Exception()


class Probe(Constraint):
    """Prints whenever connector has a new value."""

    def __init__(self, connector, name):
        self.name = name
        self.connector = connector
        self.connector.connect(self)

    def new_value(self,):
        self._print_probe(self.connector.get_value())

    def forget_value(self,):
        self._print_probe("?")

    def _print_probe(self, value):
        print("Probe: {} = {}".format(self.name, value))


class Contradiction(Exception):
    """Raised whenever a connector has an existing value but
    is asked to change a different value.

    """
    pass 
该框架在两个方向上都能很好地复制SICP中的Celcius/Fahrenheit示例,如下面的doctest所示:

>>> def celcius_fahrenheit_converter(c, f):
...     u = Connector()
...     v = Connector()
...     w = Connector()
...     x = Connector()
...     y = Connector()
...     Multiplier(c, w, u)
...     Multiplier(v, x, u)
...     Adder(v, y, f)
...     Constant(w, 9)
...     Constant(x, 5)
...     Constant(y, 32)

>>> c = Connector()
>>> f = Connector()
>>> celcius_fahrenheit_converter(c, f)
>>> c_probe = Probe(c, "Celcius temp")
>>> f_probe = Probe(f, "Fahrenheit temp")
>>> c.set_value(25.0)
Probe: Celcius temp = 25.0
Probe: Fahrenheit temp = 77.0
>>> f.set_value(212.0)
Traceback (most recent call last):
...
Contradiction: 77.0 vs. 212.0
>>> c.forget_value()
Probe: Celcius temp = ?
Probe: Fahrenheit temp = ?
>>> f.set_value(212.0)
Probe: Fahrenheit temp = 212.0
Probe: Celcius temp = 100.0
当我试图使用该框架解决一个物理问题(位移与给定加速度的时间之比)时,我注意到“反向”方向并没有在该网络中传播。我已经确定了一个简单的例子(在本例中,二次方程y=ax**2+bx+c)展示了相同的行为。如果我尝试求解
y
给定的
x
,它会完全传播,但如果我尝试求解
x
给定的
y
,它不会传播。请参阅以下doctest(探测内部连接器i1、i2和i3以说明问题):

当试图将约束从
y
传播到
x
时,运行上述doctest会产生以下错误:

Failed example:
    y.set_value(301.0)
Expected:
    Probe: y = 301.0
    Probe: i1 = 300.0
    Probe: i2 = 100.0
    Probe: i3 = 200.0
    Probe: x = 10.0
Got:
    Probe: y = 301.0
    Probe: i1 = 300.0
**********************************************************************
1 items had failures:
   1 of  47 in __main__
***Test Failed*** 1 failures.
发生的事情是,当我告诉网络忘记
x
的值时,连接
i1
i2
i3
加法器
约束忘记了所有3个值。因此,当传播沿
y
方向返回到
x
方向时,
加法器
发现了两个未知数(
i2
i3
,其中
i1
直接从
y
c
,这是已知的)。尽管等式只有一个未知项(即
x
),但不幸的是,该未知项影响了损坏的
加法器中发现的三个连接器中的两个

问题

如何修复约束网络的设计或传播算法,以确保在这个简单的二次示例中,我能够计算给定的
x
和给定的
y

我试过什么

我曾尝试研究数值约束满足算法,但没有发现任何适用于此的算法

我试图通过重新设计约束网络本身来进行推理,使得
x
仅连接到一个约束(在这个特定情况下,它可能是二次方程本身),但a)我无法在本例中实现,b这似乎不是一般解

我曾尝试通过不同的约束算法(例如搜索)进行推理,但我认为对于一个完全由等式约束定义的网络来说,这可能是过分的

Failed example:
    y.set_value(301.0)
Expected:
    Probe: y = 301.0
    Probe: i1 = 300.0
    Probe: i2 = 100.0
    Probe: i3 = 200.0
    Probe: x = 10.0
Got:
    Probe: y = 301.0
    Probe: i1 = 300.0
**********************************************************************
1 items had failures:
   1 of  47 in __main__
***Test Failed*** 1 failures.