Example #1
0
def _hessian(xs, f_x_expr):
    hess = ComponentMap()
    df_dx_map = reverse_sd(f_x_expr)
    for x in xs:
        ddf_ddx_map = reverse_sd(df_dx_map[x])
        hess[x] = ddf_ddx_map
    return hess
Example #2
0
    def test_log(self):
        m = pe.ConcreteModel()
        m.x = pe.Var(bounds=(1, 2))
        m.y = pe.Var(bounds=(1, 2))
        m.z = pe.Var()
        m.w = pe.Var()
        m.c = pe.Constraint(expr=pe.log(m.x * m.y) + m.z == 0)
        m.c2 = pe.Constraint(expr=m.w - 3 * pe.log(m.x * m.y) == 0)

        rel = coramin.relaxations.relax(m)

        self.assertTrue(hasattr(rel, 'aux_cons'))
        self.assertTrue(hasattr(rel, 'aux_vars'))
        self.assertEqual(len(rel.aux_cons), 2)
        self.assertEqual(len(rel.aux_vars), 2)

        self.assertAlmostEqual(rel.aux_vars[1].lb, 1)
        self.assertAlmostEqual(rel.aux_vars[1].ub, 4)

        self.assertAlmostEqual(rel.aux_vars[2].lb, math.log(1))
        self.assertAlmostEqual(rel.aux_vars[2].ub, math.log(4))

        self.assertEqual(rel.aux_cons[1].lower, 0)
        self.assertEqual(rel.aux_cons[1].upper, 0)
        ders = reverse_sd(rel.aux_cons[1].body)
        self.assertEqual(ders[rel.z], 1)
        self.assertEqual(ders[rel.aux_vars[2]], 1)
        self.assertEqual(len(list(identify_variables(rel.aux_cons[1].body))),
                         2)

        self.assertEqual(rel.aux_cons[2].lower, 0)
        self.assertEqual(rel.aux_cons[2].upper, 0)
        ders = reverse_sd(rel.aux_cons[2].body)
        self.assertEqual(ders[rel.w], 1)
        self.assertEqual(ders[rel.aux_vars[2]], -3)
        self.assertEqual(len(list(identify_variables(rel.aux_cons[2].body))),
                         2)

        self.assertTrue(hasattr(rel, 'relaxations'))
        self.assertTrue(hasattr(rel.relaxations, 'rel0'))
        self.assertTrue(
            isinstance(rel.relaxations.rel0,
                       coramin.relaxations.PWMcCormickRelaxation))
        self.assertIn(rel.x, ComponentSet(rel.relaxations.rel0.get_rhs_vars()))
        self.assertIn(rel.y, ComponentSet(rel.relaxations.rel0.get_rhs_vars()))
        self.assertEqual(id(rel.aux_vars[1]),
                         id(rel.relaxations.rel0.get_aux_var()))

        self.assertTrue(hasattr(rel.relaxations, 'rel1'))
        self.assertTrue(
            isinstance(rel.relaxations.rel1,
                       coramin.relaxations.PWUnivariateRelaxation))
        self.assertIn(rel.aux_vars[1],
                      ComponentSet(rel.relaxations.rel1.get_rhs_vars()))
        self.assertEqual(id(rel.aux_vars[2]),
                         id(rel.relaxations.rel1.get_aux_var()))
        self.assertFalse(rel.relaxations.rel1.is_rhs_convex())
        self.assertTrue(rel.relaxations.rel1.is_rhs_concave())

        self.assertFalse(hasattr(rel.relaxations, 'rel2'))
Example #3
0
    def test_cubic(self):
        m = pe.ConcreteModel()
        m.x = pe.Var(bounds=(-1,1))
        m.y = pe.Var()
        m.z = pe.Var()
        m.w = pe.Var()
        m.c = pe.Constraint(expr=m.x**3 + m.y + m.z == 0)
        m.c2 = pe.Constraint(expr=m.w - 3*m.x**3 == 0)

        rel = coramin.relaxations.relax(m)

        # this problem should turn into
        #
        # aux2 + y + z = 0        => aux_con[1]
        # w - 3*aux2 = 0          => aux_con[2]
        # aux1 = x**2             => rel0
        # aux2 = x*aux1           => rel1

        self.assertTrue(hasattr(rel, 'aux_cons'))
        self.assertTrue(hasattr(rel, 'aux_vars'))
        self.assertEqual(len(rel.aux_cons), 2)
        self.assertEqual(len(rel.aux_vars), 2)

        self.assertAlmostEqual(rel.aux_vars[1].lb, 0)
        self.assertAlmostEqual(rel.aux_vars[1].ub, 1)

        self.assertAlmostEqual(rel.aux_vars[2].lb, -1)
        self.assertAlmostEqual(rel.aux_vars[2].ub, 1)

        self.assertEqual(rel.aux_cons[1].lower, 0)
        self.assertEqual(rel.aux_cons[1].upper, 0)
        ders = reverse_sd(rel.aux_cons[1].body)
        self.assertEqual(ders[rel.z], 1)
        self.assertEqual(ders[rel.aux_vars[2]], 1)
        self.assertEqual(ders[rel.y], 1)
        self.assertEqual(len(list(identify_variables(rel.aux_cons[1].body))), 3)

        self.assertEqual(rel.aux_cons[2].lower, 0)
        self.assertEqual(rel.aux_cons[2].upper, 0)
        ders = reverse_sd(rel.aux_cons[2].body)
        self.assertEqual(ders[rel.w], 1)
        self.assertEqual(ders[rel.aux_vars[2]], -3)
        self.assertEqual(len(list(identify_variables(rel.aux_cons[2].body))), 2)

        self.assertTrue(hasattr(rel, 'relaxations'))
        self.assertTrue(hasattr(rel.relaxations, 'rel0'))
        self.assertTrue(isinstance(rel.relaxations.rel0, coramin.relaxations.PWXSquaredRelaxation))
        self.assertIn(rel.x, ComponentSet(rel.relaxations.rel0.get_rhs_vars()))
        self.assertEqual(id(rel.aux_vars[1]), id(rel.relaxations.rel0.get_aux_var()))

        self.assertTrue(hasattr(rel.relaxations, 'rel1'))
        self.assertTrue(isinstance(rel.relaxations.rel1, coramin.relaxations.PWMcCormickRelaxation))
        self.assertIn(rel.x, ComponentSet(rel.relaxations.rel1.get_rhs_vars()))
        self.assertIn(rel.aux_vars[1], ComponentSet(rel.relaxations.rel1.get_rhs_vars()))
        self.assertEqual(id(rel.aux_vars[2]), id(rel.relaxations.rel1.get_aux_var()))
Example #4
0
    def test_product3(self):
        m = pe.ConcreteModel()
        m.x = pe.Var(bounds=(-1,1))
        m.y = pe.Var(bounds=(-1,1))
        m.z = pe.Var()
        m.c = pe.Constraint(expr=m.z - m.x*m.y*3 == 0)

        rel = coramin.relaxations.relax(m)

        self.assertTrue(hasattr(rel, 'aux_cons'))
        self.assertTrue(hasattr(rel, 'aux_vars'))
        self.assertEqual(len(rel.aux_cons), 1)
        self.assertEqual(len(rel.aux_vars), 1)
        self.assertAlmostEqual(rel.aux_vars[1].lb, -1)
        self.assertAlmostEqual(rel.aux_vars[1].ub, 1)
        self.assertEqual(rel.aux_cons[1].lower, 0)
        self.assertEqual(rel.aux_cons[1].upper, 0)
        ders = reverse_sd(rel.aux_cons[1].body)
        self.assertEqual(ders[rel.z], 1)
        self.assertEqual(ders[rel.aux_vars[1]], -3)
        self.assertEqual(len(list(identify_variables(rel.aux_cons[1].body))), 2)

        self.assertTrue(hasattr(rel, 'relaxations'))
        self.assertTrue(hasattr(rel.relaxations, 'rel0'))
        self.assertTrue(isinstance(rel.relaxations.rel0, coramin.relaxations.PWMcCormickRelaxation))
        self.assertIn(rel.x, ComponentSet(rel.relaxations.rel0.get_rhs_vars()))
        self.assertIn(rel.y, ComponentSet(rel.relaxations.rel0.get_rhs_vars()))
        self.assertEqual(id(rel.aux_vars[1]), id(rel.relaxations.rel0.get_aux_var()))
        relaxations = list(coramin.relaxations.relaxation_data_objects(rel))
        self.assertEqual(len(relaxations), 1)
Example #5
0
    def test_pow_neg_odd2(self):
        m = pe.ConcreteModel()
        m.x = pe.Var(bounds=(-2, -1))
        m.y = pe.Var()
        m.z = pe.Var()
        m.w = pe.Var()
        m.p = pe.Param(initialize=-3)
        m.c = pe.Constraint(expr=m.x**m.p + m.y + m.z == 0)
        m.c2 = pe.Constraint(expr=m.w - 3 * m.x**m.p == 0)

        rel = coramin.relaxations.relax(m)

        self.assertTrue(hasattr(rel, 'aux_cons'))
        self.assertTrue(hasattr(rel, 'aux_vars'))
        self.assertEqual(len(rel.aux_cons), 2)
        self.assertEqual(len(rel.aux_vars), 1)
        self.assertAlmostEqual(rel.aux_vars[1].lb, -1)
        self.assertAlmostEqual(rel.aux_vars[1].ub, -0.125)

        self.assertEqual(rel.aux_cons[1].lower, 0)
        self.assertEqual(rel.aux_cons[1].upper, 0)
        ders = reverse_sd(rel.aux_cons[1].body)
        self.assertEqual(ders[rel.z], 1)
        self.assertEqual(ders[rel.aux_vars[1]], 1)
        self.assertEqual(ders[rel.y], 1)
        self.assertEqual(len(list(identify_variables(rel.aux_cons[1].body))),
                         3)

        self.assertEqual(rel.aux_cons[2].lower, 0)
        self.assertEqual(rel.aux_cons[2].upper, 0)
        ders = reverse_sd(rel.aux_cons[2].body)
        self.assertEqual(ders[rel.w], 1)
        self.assertEqual(ders[rel.aux_vars[1]], -3)
        self.assertEqual(len(list(identify_variables(rel.aux_cons[2].body))),
                         2)

        self.assertTrue(hasattr(rel, 'relaxations'))
        self.assertTrue(hasattr(rel.relaxations, 'rel0'))
        self.assertTrue(
            isinstance(rel.relaxations.rel0,
                       coramin.relaxations.PWUnivariateRelaxation))
        self.assertIn(rel.x, ComponentSet(rel.relaxations.rel0.get_rhs_vars()))
        self.assertEqual(id(rel.aux_vars[1]),
                         id(rel.relaxations.rel0.get_aux_var()))
        self.assertFalse(rel.relaxations.rel0.is_rhs_convex())
        self.assertTrue(rel.relaxations.rel0.is_rhs_concave())
        self.assertFalse(hasattr(rel.relaxations, 'rel1'))
Example #6
0
 def test_exp(self):
     m = pyo.ConcreteModel()
     m.x = pyo.Var(initialize=2.0)
     e = pyo.exp(m.x)
     derivs = reverse_ad(e)
     symbolic = reverse_sd(e)
     self.assertAlmostEqual(derivs[m.x], pyo.value(symbolic[m.x]), tol + 3)
     self.assertAlmostEqual(derivs[m.x], approx_deriv(e, m.x), tol)
Example #7
0
 def test_acos(self):
     m = pe.ConcreteModel()
     m.x = pe.Var(initialize=0.5)
     e = pe.acos(m.x)
     derivs = reverse_ad(e)
     symbolic = reverse_sd(e)
     self.assertAlmostEqual(derivs[m.x], pe.value(symbolic[m.x]), tol + 3)
     self.assertAlmostEqual(derivs[m.x], approx_deriv(e, m.x), tol)
Example #8
0
 def test_NPV(self):
     m = pyo.ConcreteModel()
     m.p = pyo.Param(initialize=2.0, mutable=True)
     e = pyo.log(m.p)
     derivs = reverse_ad(e)
     symbolic = reverse_sd(e)
     self.assertAlmostEqual(derivs[m.p], pyo.value(symbolic[m.p]), tol)
     self.assertAlmostEqual(derivs[m.p], approx_deriv(e, m.p), tol)
Example #9
0
 def test_abs(self):
     m = pyo.ConcreteModel()
     m.x = pyo.Var(initialize=2.0)
     e = 2 * abs(m.x)
     with self.assertRaisesRegexp(
             DifferentiationException,
             r'Cannot perform symbolic differentiation of abs\(x\)'):
         reverse_sd(e)
     derivs = reverse_ad(e)
     self.assertAlmostEqual(derivs[m.x], approx_deriv(e, m.x), tol)
     m.x.value = -2
     derivs = reverse_ad(e)
     self.assertAlmostEqual(derivs[m.x], approx_deriv(e, m.x), tol)
     m.x.value = 0
     with self.assertRaisesRegexp(DifferentiationException,
                                  r'Cannot differentiate abs\(x\) at x=0'):
         reverse_ad(e)
Example #10
0
    def test_quadratic(self):
        m = pe.ConcreteModel()
        m.x = pe.Var(bounds=(-1, 1))
        m.y = pe.Var()
        m.z = pe.Var()
        m.w = pe.Var()
        m.c = pe.Constraint(expr=m.x**2 + m.y + m.z == 0)
        m.c2 = pe.Constraint(expr=m.w - 3 * m.x**2 == 0)

        rel = coramin.relaxations.relax(m)

        self.assertTrue(hasattr(rel, 'aux_cons'))
        self.assertTrue(hasattr(rel, 'aux_vars'))
        self.assertEqual(len(rel.aux_cons), 2)
        self.assertEqual(len(rel.aux_vars), 1)
        self.assertAlmostEqual(rel.aux_vars[1].lb, 0)
        self.assertAlmostEqual(rel.aux_vars[1].ub, 1)

        self.assertEqual(rel.aux_cons[1].lower, 0)
        self.assertEqual(rel.aux_cons[1].upper, 0)
        ders = reverse_sd(rel.aux_cons[1].body)
        self.assertEqual(ders[rel.z], 1)
        self.assertEqual(ders[rel.aux_vars[1]], 1)
        self.assertEqual(ders[rel.y], 1)
        self.assertEqual(len(list(identify_variables(rel.aux_cons[1].body))),
                         3)

        self.assertEqual(rel.aux_cons[2].lower, 0)
        self.assertEqual(rel.aux_cons[2].upper, 0)
        ders = reverse_sd(rel.aux_cons[2].body)
        self.assertEqual(ders[rel.w], 1)
        self.assertEqual(ders[rel.aux_vars[1]], -3)
        self.assertEqual(len(list(identify_variables(rel.aux_cons[2].body))),
                         2)

        self.assertTrue(hasattr(rel, 'relaxations'))
        self.assertTrue(hasattr(rel.relaxations, 'rel0'))
        self.assertTrue(
            isinstance(rel.relaxations.rel0,
                       coramin.relaxations.PWXSquaredRelaxation))
        self.assertIn(rel.x, ComponentSet(rel.relaxations.rel0.get_rhs_vars()))
        self.assertEqual(id(rel.aux_vars[1]),
                         id(rel.relaxations.rel0.get_aux_var()))
        self.assertFalse(hasattr(rel.relaxations, 'rel1'))
Example #11
0
 def test_sum(self):
     m = pyo.ConcreteModel()
     m.x = pyo.Var(initialize=2.0)
     m.y = pyo.Var(initialize=3.0)
     e = 2.0 * m.x + 3.0 * m.y - m.x * m.y
     derivs = reverse_ad(e)
     symbolic = reverse_sd(e)
     self.assertAlmostEqual(derivs[m.x], pyo.value(symbolic[m.x]), tol + 3)
     self.assertAlmostEqual(derivs[m.y], pyo.value(symbolic[m.y]), tol + 3)
     self.assertAlmostEqual(derivs[m.x], approx_deriv(e, m.x), tol)
     self.assertAlmostEqual(derivs[m.y], approx_deriv(e, m.y), tol)
Example #12
0
 def test_linear_expression(self):
     m = pyo.ConcreteModel()
     m.x = pyo.Var(initialize=2.0)
     m.y = pyo.Var(initialize=3.0)
     m.p = pyo.Param(initialize=2.5, mutable=True)
     e = LinearExpression(constant=m.p, linear_vars=[m.x, m.y], linear_coefs=[1.8, m.p])
     e = pyo.log(e)
     derivs = reverse_ad(e)
     symbolic = reverse_sd(e)
     for v in [m.x, m.y, m.p]:
         self.assertAlmostEqual(derivs[v], pyo.value(symbolic[v]), tol)
         self.assertAlmostEqual(derivs[v], approx_deriv(e, v), tol)
Example #13
0
 def test_nested_named_expressions(self):
     m = pyo.ConcreteModel()
     m.x = pyo.Var(initialize=0.23)
     m.y = pyo.Var(initialize=0.88)
     m.a = pyo.Expression(expr=(m.x + 1)**2)
     m.b = pyo.Expression(expr=3 * (m.a + m.y))
     e = 2 * m.a + 2 * m.b + 2 * m.b + 2 * m.a
     derivs = reverse_ad(e)
     symbolic = reverse_sd(e)
     self.assertAlmostEqual(derivs[m.x], pyo.value(symbolic[m.x]), tol + 3)
     self.assertAlmostEqual(derivs[m.x], approx_deriv(e, m.x), tol)
     self.assertAlmostEqual(derivs[m.y], pyo.value(symbolic[m.y]), tol + 3)
     self.assertAlmostEqual(derivs[m.y], approx_deriv(e, m.y), tol)
Example #14
0
 def test_duplicate_expressions(self):
     m = pyo.ConcreteModel()
     m.x = pyo.Var(initialize=0.23)
     m.y = pyo.Var(initialize=0.88)
     a = (m.x + 1)**2
     b = 3 * (a + m.y)
     e = 2 * a + 2 * b + 2 * b + 2 * a
     derivs = reverse_ad(e)
     symbolic = reverse_sd(e)
     self.assertAlmostEqual(derivs[m.x], pyo.value(symbolic[m.x]), tol + 3)
     self.assertAlmostEqual(derivs[m.x], approx_deriv(e, m.x), tol)
     self.assertAlmostEqual(derivs[m.y], pyo.value(symbolic[m.y]), tol + 3)
     self.assertAlmostEqual(derivs[m.y], approx_deriv(e, m.y), tol)
Example #15
0
 def test_multiple_named_expressions(self):
     m = pyo.ConcreteModel()
     m.x = pyo.Var()
     m.y = pyo.Var()
     m.x.value = 1
     m.y.value = 1
     m.E = pyo.Expression(expr=m.x * m.y)
     e = m.E - m.E
     derivs = reverse_ad(e)
     self.assertAlmostEqual(derivs[m.x], 0)
     self.assertAlmostEqual(derivs[m.y], 0)
     symbolic = reverse_sd(e)
     self.assertAlmostEqual(pyo.value(symbolic[m.x]), 0)
     self.assertAlmostEqual(pyo.value(symbolic[m.y]), 0)
Example #16
0
 def test_nested(self):
     m = pyo.ConcreteModel()
     m.x = pyo.Var(initialize=2)
     m.y = pyo.Var(initialize=3)
     m.p = pyo.Param(initialize=0.5, mutable=True)
     e = pyo.exp(m.x**m.p + 3.2 * m.y - 12)
     derivs = reverse_ad(e)
     symbolic = reverse_sd(e)
     self.assertAlmostEqual(derivs[m.x], pyo.value(symbolic[m.x]), tol + 3)
     self.assertAlmostEqual(derivs[m.y], pyo.value(symbolic[m.y]), tol + 3)
     self.assertAlmostEqual(derivs[m.p], pyo.value(symbolic[m.p]), tol + 3)
     self.assertAlmostEqual(derivs[m.x], approx_deriv(e, m.x), tol)
     self.assertAlmostEqual(derivs[m.y], approx_deriv(e, m.y), tol)
     self.assertAlmostEqual(derivs[m.p], approx_deriv(e, m.p), tol)
Example #17
0
    def test_expressiondata(self):
        m = pyo.ConcreteModel()
        m.x = pyo.Var(initialize=3)
        m.e = pyo.Expression(expr=m.x * 2)

        @m.Expression([1, 2])
        def e2(m, i):
            if i == 1:
                return m.x + 4
            else:
                return m.x ** 2
        m.o = pyo.Objective(expr=m.e + 1 + m.e2[1] + m.e2[2])
        derivs = reverse_ad(m.o.expr)
        symbolic = reverse_sd(m.o.expr)
        self.assertAlmostEqual(derivs[m.x], pyo.value(symbolic[m.x]), tol)
Example #18
0
def _check_coefficents(comp, expr, too_large, too_small, largs_coef_map,
                       small_coef_map):
    ders = reverse_sd(expr)
    for _v, _der in ders.items():
        if isinstance(_v, _GeneralVarData):
            if _v.is_fixed():
                continue
            der_lb, der_ub = compute_bounds_on_expr(_der)
            der_lb, der_ub = _bounds_to_float(der_lb, der_ub)
            if der_lb <= -too_large or der_ub >= too_large:
                if comp not in largs_coef_map:
                    largs_coef_map[comp] = list()
                largs_coef_map[comp].append((_v, der_lb, der_ub))
            if abs(der_lb) <= too_small and abs(der_ub) < too_small:
                if der_lb != 0 or der_ub != 0:
                    if comp not in small_coef_map:
                        small_coef_map[comp] = list()
                    small_coef_map[comp].append((_v, der_lb, der_ub))
Example #19
0
 def __init__(self, expr, x):
     self._expr = expr
     self._x = x
     self._deriv = reverse_sd(expr)[x]
Example #20
0
    def test_pow_neg(self):
        m = pe.ConcreteModel()
        m.x = pe.Var(bounds=(-1,1))
        m.y = pe.Var()
        m.z = pe.Var()
        m.w = pe.Var()
        m.p = pe.Param(initialize=-2)
        m.c = pe.Constraint(expr=m.x**m.p + m.y + m.z == 0)
        m.c2 = pe.Constraint(expr=m.w - 3*m.x**m.p == 0)

        rel = coramin.relaxations.relax(m)

        # This model should be relaxed to
        #
        # aux2 + y + z = 0
        # w - 3 * aux2 = 0
        # aux1 = x**2
        # aux1*aux2 = aux3
        # aux3 = 1
        #

        self.assertTrue(hasattr(rel, 'aux_cons'))
        self.assertTrue(hasattr(rel, 'aux_vars'))
        self.assertEqual(len(rel.aux_cons), 2)
        self.assertEqual(len(rel.aux_vars), 3)

        self.assertAlmostEqual(rel.aux_vars[1].lb, 0)
        self.assertAlmostEqual(rel.aux_vars[1].ub, 1)

        self.assertTrue(rel.aux_vars[3].is_fixed())
        self.assertEqual(rel.aux_vars[3].value, 1)

        self.assertEqual(rel.aux_cons[1].lower, 0)
        self.assertEqual(rel.aux_cons[1].upper, 0)
        ders = reverse_sd(rel.aux_cons[1].body)
        self.assertEqual(ders[rel.z], 1)
        self.assertEqual(ders[rel.aux_vars[2]], 1)
        self.assertEqual(ders[rel.y], 1)
        self.assertEqual(len(list(identify_variables(rel.aux_cons[1].body))), 3)

        self.assertEqual(rel.aux_cons[2].lower, 0)
        self.assertEqual(rel.aux_cons[2].upper, 0)
        ders = reverse_sd(rel.aux_cons[2].body)
        self.assertEqual(ders[rel.w], 1)
        self.assertEqual(ders[rel.aux_vars[2]], -3)
        self.assertEqual(len(list(identify_variables(rel.aux_cons[2].body))), 2)

        self.assertTrue(hasattr(rel, 'relaxations'))
        self.assertTrue(hasattr(rel.relaxations, 'rel0'))
        self.assertTrue(isinstance(rel.relaxations.rel0, coramin.relaxations.PWXSquaredRelaxation))
        self.assertIn(rel.x, ComponentSet(rel.relaxations.rel0.get_rhs_vars()))
        self.assertEqual(id(rel.aux_vars[1]), id(rel.relaxations.rel0.get_aux_var()))
        self.assertTrue(rel.relaxations.rel0.is_rhs_convex())
        self.assertFalse(rel.relaxations.rel0.is_rhs_concave())

        self.assertTrue(hasattr(rel.relaxations, 'rel1'))
        self.assertTrue(isinstance(rel.relaxations.rel1, coramin.relaxations.PWMcCormickRelaxation))
        self.assertIn(rel.aux_vars[1], ComponentSet(rel.relaxations.rel1.get_rhs_vars()))
        self.assertIn(rel.aux_vars[2], ComponentSet(rel.relaxations.rel1.get_rhs_vars()))
        self.assertEqual(id(rel.aux_vars[3]), id(rel.relaxations.rel1.get_aux_var()))

        self.assertFalse(hasattr(rel.relaxations, 'rel2'))