Subject: [Ufl] Support for tan(x)

From: Harish Narayanan <[email-masked]>
Date: Tue, 16 Mar 2010 15:34:15 +0100

I have attached a patch which adds support for evaluating and
differentiating tan(f) in UFL forms. If this is acceptable, I plan to
similarly add inverse trigonometric functions.

Harish
# Bazaar merge directive format 2 (Bazaar 0.90)
# revision_id: [email-masked]
# target_branch: bzr+ssh://bazaar.launchpad.net/~ufl-core/ufl/main/
# testament_sha1: b8523f9c30cb9e70ef5f736b0c5b00d198551b87
# timestamp: 2010-03-16 15:31:38 +0100
# base_revision_id: [email-masked]\
#   xgpckx7p1jdwh523
# 
# Begin patch
=== modified file 'TODO'
--- TODO	2009-12-08 15:59:29 +0000
+++ TODO	2010-03-16 14:27:18 +0000
@@ -145,7 +145,6 @@
       even if f is defined on a quadrature element.
 
 # Additional operators (low priority):
-    - tan(f)
     - log(f, base)
     - other mathematical functions in <cmath> (need derivatives)
     - bessel functions (needs non-standard library code)

=== modified file 'test/derivative.py'
--- test/derivative.py	2010-02-07 21:15:52 +0000
+++ test/derivative.py	2010-03-16 14:27:18 +0000
@@ -91,6 +91,11 @@
         def df(w, v): return -v*sin(w)
         self._test(f, df)
 
+    def testTan(self):
+        def f(w):  return tan(w)
+        def df(w, v): return v*2.0/(cos(2.0*w) + 1.0)
+        self._test(f, df)
+
     def testIndexSum(self):
         def f(w):
             # 3*w + 4*w**2 + 5*w**3

=== modified file 'test/diff.py'
--- test/diff.py	2009-03-27 13:03:47 +0000
+++ test/diff.py	2010-03-16 14:27:18 +0000
@@ -83,6 +83,11 @@
         def df(v): return -sin(v)
         self._test(f, df)
 
+    def testTan(self):
+        def f(v):  return tan(v)
+        def df(v): return 2.0/(cos(2.0*v) + 1.0)
+        self._test(f, df)
+
     def testIndexSum(self):
         def f(v):
             # 3*v + 4*v**2 + 5*v**3

=== modified file 'test/evaluate.py'
--- test/evaluate.py	2009-12-08 15:59:29 +0000
+++ test/evaluate.py	2010-03-16 14:27:18 +0000
@@ -120,6 +120,11 @@
         e = s((5,7))
         v = math.cos(5)
         self.assertTrue(e == v)
+
+        s = tan(x)
+        e = s((5,7))
+        v = math.tan(5)
+        self.assertTrue(e == v)
         
         s = ln(x)
         e = s((5,7))

=== modified file 'ufl/__init__.py'
--- ufl/__init__.py	2010-02-15 15:12:43 +0000
+++ ufl/__init__.py	2010-03-16 14:27:18 +0000
@@ -80,7 +80,7 @@
     Dx, grad, div, curl, rot, Dn
 
 Nonlinear functions:
-    abs, sign, sqrt, exp, ln, cos, sin,
+    abs, sign, sqrt, exp, ln, cos, sin, tan
 
 Discontinuous Galerkin operators:
     jump, avg, v('+'), v('-')
@@ -163,7 +163,7 @@
                        outer, inner, dot, cross, \
                        det, inv, cofac, \
                        transpose, tr, dev, skew, sym, \
-                       sqrt, exp, ln, cos, sin, \
+                       sqrt, exp, ln, cos, sin, tan, \
                        eq, ne, le, ge, lt, gt, conditional, sign, \
                        variable, diff, \
                        Dx,  grad, div, curl, rot, Dn, \

=== modified file 'ufl/algorithms/forward_ad.py'
--- ufl/algorithms/forward_ad.py	2009-12-08 15:59:29 +0000
+++ ufl/algorithms/forward_ad.py	2010-03-16 14:27:18 +0000
@@ -23,7 +23,7 @@
 from ufl.algebra import Sum, Product, Division, Power, Abs
 from ufl.tensoralgebra import Transposed, Outer, Inner, Dot, Cross, Trace, \
     Determinant, Inverse, Deviatoric, Cofactor
-from ufl.mathfunctions import MathFunction, Sqrt, Exp, Ln, Cos, Sin
+from ufl.mathfunctions import MathFunction, Sqrt, Exp, Ln, Cos, Sin, Tan
 from ufl.restriction import Restricted, PositiveRestricted, NegativeRestricted
 from ufl.differentiation import Derivative, FunctionDerivative,\
     SpatialDerivative, VariableDerivative
@@ -33,7 +33,7 @@
 #from ufl.classes import ufl_classes, terminal_classes, nonterminal_classes
 from ufl.classes import terminal_classes
 from ufl.operators import dot, inner, outer, lt, eq, conditional, sign
-from ufl.operators import sqrt, exp, ln, cos, sin
+from ufl.operators import sqrt, exp, ln, cos, sin, tan
 from ufl.algorithms.traversal import iter_expressions
 from ufl.algorithms.analysis import extract_type
 from ufl.algorithms.transformations import expand_compounds, Transformer, transform, transform_integrands
@@ -402,6 +402,12 @@
         op = fp*cos(f)
         return (o, op)
 
+    def tan(self, o, a):
+        f, fp = a
+        o = self.reuse_if_possible(o, f)
+        op = fp*2.0/(cos(2.0*f) + 1.0)
+        return (o, op)
+
     # --- Restrictions
 
     def positive_restricted(self, o, a):

=== modified file 'ufl/algorithms/pdiffs.py'
--- ufl/algorithms/pdiffs.py	2009-04-25 09:47:05 +0000
+++ ufl/algorithms/pdiffs.py	2010-03-16 14:27:18 +0000
@@ -7,7 +7,7 @@
 from ufl.log import error
 from ufl.assertions import ufl_assert
 from ufl.classes import Zero, IntValue, FloatValue
-from ufl.operators import sin, cos, exp, ln, sqrt, conditional, sign
+from ufl.operators import sin, cos, tan, exp, ln, sqrt, conditional, sign
 from ufl.tensors import unit_vectors, ListTensor
 from ufl.algorithms.transformations import MultiFunction
 
@@ -100,6 +100,11 @@
         "d/dx sin x = cos(x)"
         x, = f.operands()
         return (cos(x),)
+
+    def tan(self, f):
+        "d/dx tan x = (sec(x))^2 = 2/(cos(2x) + 1)"
+        x, = f.operands()
+        return (2.0/(cos(2.0*x) + 1.0),)
     
     # --- Shape and indexing manipulators

=== modified file 'ufl/algorithms/ufl2latex.py'
--- ufl/algorithms/ufl2latex.py	2010-02-07 21:15:52 +0000
+++ ufl/algorithms/ufl2latex.py	2010-03-16 14:27:18 +0000
@@ -22,7 +22,7 @@
 from ufl.algebra import Sum, Product, Division, Power, Abs
 from ufl.indexsum import IndexSum
 from ufl.tensoralgebra import Transposed, Outer, Inner, Dot, Cross, Trace, Determinant, Inverse, Deviatoric, Cofactor
-from ufl.mathfunctions import Sqrt, Exp, Ln, Cos, Sin
+from ufl.mathfunctions import Sqrt, Exp, Ln, Cos, Sin, Tan
 from ufl.restriction import PositiveRestricted, NegativeRestricted
 from ufl.differentiation import SpatialDerivative, VariableDerivative, Grad, Div, Curl
 from ufl.conditional import EQ, NE, LE, GE, LT, GT, Conditional
@@ -206,6 +206,9 @@
     def sin(self, o, f):
         return r"\sin{%s}" % par(f)
 
+    def tan(self, o, f):
+        return r"\tan{%s}" % par(f)
+
     def power(self, o, a, b):
         return "{%s}^{%s}" % (par(a), par(b))

=== modified file 'ufl/classes.py'
--- ufl/classes.py	2009-12-08 15:59:29 +0000
+++ ufl/classes.py	2010-03-16 14:27:18 +0000
@@ -21,7 +21,7 @@
 from ufl.tensors import ListTensor, ComponentTensor
 from ufl.algebra import Sum, Product, Division, Power, Abs
 from ufl.tensoralgebra import CompoundTensorOperator, Transposed, Outer, Inner, Dot, Cross, Trace, Determinant, Cofactor, Inverse, Deviatoric, Skew, Sym
-from ufl.mathfunctions import MathFunction, Sqrt, Exp, Ln, Cos, Sin
+from ufl.mathfunctions import MathFunction, Sqrt, Exp, Ln, Cos, Sin, Tan
 from ufl.restriction import Restricted, PositiveRestricted, NegativeRestricted
 from ufl.lifting import LiftingResult, LiftingOperatorResult, LiftingFunctionResult, TerminalOperator, LiftingOperator, LiftingFunction
 from ufl.differentiation import Derivative, CompoundDerivative, FunctionDerivative, SpatialDerivative, VariableDerivative, Grad, Div, Curl

=== modified file 'ufl/mathfunctions.py'
--- ufl/mathfunctions.py	2009-04-21 07:40:04 +0000
+++ ufl/mathfunctions.py	2010-03-16 14:27:18 +0000
@@ -94,3 +94,11 @@
     def __init__(self, argument):
         MathFunction.__init__(self, "sin", argument)
 
+class Tan(MathFunction):
+    def __new__(cls, argument):
+        if isinstance(argument, (ScalarValue, Zero)):
+            return FloatValue(math.tan(float(argument)))
+        return MathFunction.__new__(cls)
+    
+    def __init__(self, argument):
+        MathFunction.__init__(self, "tan", argument)

=== modified file 'ufl/operators.py'
--- ufl/operators.py	2009-12-01 17:09:26 +0000
+++ ufl/operators.py	2010-03-16 14:27:18 +0000
@@ -16,7 +16,7 @@
 from ufl.variable import Variable
 from ufl.tensors import as_tensor
 from ufl.conditional import EQ, NE, LE, GE, LT, GT, Conditional
-from ufl.mathfunctions import Sqrt, Exp, Ln, Cos, Sin
+from ufl.mathfunctions import Sqrt, Exp, Ln, Cos, Sin, Tan
 from ufl.indexing import indices
 from ufl.geometry import SpatialCoordinate
 
@@ -283,3 +283,6 @@
     "The sinus of f."
     return _mathfunction(f, Sin, math.sin)
 
+def tan(f):
+    "The tangent of f."
+    return _mathfunction(f, Tan, math.tan)

# Begin bundle

---

From: Anders Logg <[email-masked]>
Date: Tue, 16 Mar 2010 16:17:00 +0100

Sounds good to me. Go ahead and add the others. Maybe you could throw
in some Bessel functions as well. :-)

I'll push it later (if no one else has pushed it by then).

--
Anders

> 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
> 50A=

> _______________________________________________
> Mailing list: https://launchpad.net/~ufl
> Post to     : [email-masked]
> Unsubscribe : https://launchpad.net/~ufl
> More help   : https://help.launchpad.net/ListHelp

---

From: Harish Narayanan <[email-masked]>
Date: Wed, 17 Mar 2010 16:22:40 +0100

Attached is a patch for inverse trig functions (subsumes the previous
patch). asin(x)/acos(x) evaluate to real numbers for a very narrow range
of x, so I have disabled their tests for now.

Also attached is a small FFC patch to expose this functionality.

Harish

# Bazaar merge directive format 2 (Bazaar 0.90)
# revision_id: [email-masked]
# target_branch: bzr+ssh://bazaar.launchpad.net/~ufl-core/ufl/main/
# testament_sha1: d8a8805513f63a0e12434813ac19fd529dd54d26
# timestamp: 2010-03-17 16:17:05 +0100
# base_revision_id: [email-masked]\
#   xgpckx7p1jdwh523
# 
# Begin patch
=== modified file 'TODO'
--- TODO	2009-12-08 15:59:29 +0000
+++ TODO	2010-03-16 14:27:18 +0000
@@ -145,7 +145,6 @@
       even if f is defined on a quadrature element.

=== modified file 'test/derivative.py'
--- test/derivative.py	2010-02-07 21:15:52 +0000
+++ test/derivative.py	2010-03-17 15:16:37 +0000
@@ -81,14 +81,35 @@
         def df(w, v): return v / w
         self._test(f, df)
 
+    def testCos(self):
+        def f(w):  return cos(w)
+        def df(w, v): return -v*sin(w)
+        self._test(f, df)
+
     def testSin(self):
         def f(w):  return sin(w)
         def df(w, v): return v*cos(w)
         self._test(f, df)
 
-    def testCos(self):
-        def f(w):  return cos(w)
-        def df(w, v): return -v*sin(w)
+    def testTan(self):
+        def f(w):  return tan(w)
+        def df(w, v): return v*2.0/(cos(2.0*w) + 1.0)
+        self._test(f, df)
+
+# TODO: Check the following tests. They run into strange math domain errors.
+#     def testAcos(self):
+#         def f(w):  return acos(w)
+#         def df(w, v): return -v/sqrt(1.0 - w**2)
+#         self._test(f, df)
+
+#     def testAsin(self):
+#         def f(w):  return asin(w)
+#         def df(w, v): return v/sqrt(1.0 - w**2)
+#         self._test(f, df)
+
+    def testAtan(self):
+        def f(w):  return atan(w)
+        def df(w, v): return v/(1.0 + w**2)
         self._test(f, df)
 
     def testIndexSum(self):

=== modified file 'test/diff.py'
--- test/diff.py	2009-03-27 13:03:47 +0000
+++ test/diff.py	2010-03-17 15:16:37 +0000
@@ -83,6 +83,27 @@
         def df(v): return -sin(v)
         self._test(f, df)
 
+    def testTan(self):
+        def f(v):  return tan(v)
+        def df(v): return 2.0/(cos(2.0*v) + 1.0)
+        self._test(f, df)
+
+# TODO: Check the following tests. They run into strange math domain errors.
+#     def testAsin(self):
+#         def f(v):  return asin(v)
+#         def df(v): return 1/sqrt(1.0 - v**2)
+#         self._test(f, df)
+
+#     def testAcos(self):
+#         def f(v):  return acos(v)
+#         def df(v): return -1/sqrt(1.0 - v**2)
+#         self._test(f, df)
+
+    def testAtan(self):
+        def f(v):  return atan(v)
+        def df(v): return 1/(1.0 + v**2)
+        self._test(f, df)
+
     def testIndexSum(self):
         def f(v):
             # 3*v + 4*v**2 + 5*v**3

         s = ln(x)
         e = s((5,7))

=== modified file 'ufl/__init__.py'
--- ufl/__init__.py	2010-02-15 15:12:43 +0000
+++ ufl/__init__.py	2010-03-17 15:16:37 +0000
@@ -80,7 +80,7 @@
     Dx, grad, div, curl, rot, Dn
 
 Nonlinear functions:
-    abs, sign, sqrt, exp, ln, cos, sin,
+    abs, sign, sqrt, exp, ln, cos, sin, tan, acos, asin, atan
 
+                       sqrt, exp, ln, cos, sin, tan, acos, asin, atan, \

+++ ufl/algorithms/forward_ad.py	2010-03-17 15:16:37 +0000
+from ufl.mathfunctions import MathFunction, Sqrt, Exp, Ln, Cos, Sin, Tan, Acos, Asin, Atan
+from ufl.operators import sqrt, exp, ln, cos, sin, tan, acos, asin, atan
@@ -402,6 +402,30 @@
         op = fp*cos(f)
         return (o, op)
 
+    def tan(self, o, a):
+        f, fp = a
+        o = self.reuse_if_possible(o, f)
+        op = fp*2.0/(cos(2.0*f) + 1.0)
+        return (o, op)
+
+    def acos(self, o, a):
+        f, fp = a
+        o = self.reuse_if_possible(o, f)
+        op = -fp/sqrt(1.0 - f**2)
+        return (o, op)
+
+    def asin(self, o, a):
+        f, fp = a
+        o = self.reuse_if_possible(o, f)
+        op = fp/sqrt(1.0 - f**2)
+        return (o, op)
+
+    def atan(self, o, a):
+        f, fp = a
+        o = self.reuse_if_possible(o, f)
+        op = fp/(1.0 + f**2)
+        return (o, op)
+
     # --- Restrictions

+++ ufl/algorithms/pdiffs.py	2010-03-17 15:16:37 +0000
+from ufl.operators import cos, sin, tan, acos, asin, atan, exp, ln, sqrt, conditional, sign
 
@@ -100,6 +100,26 @@
         "d/dx sin x = cos(x)"
         x, = f.operands()
         return (cos(x),)
+
+    def tan(self, f):
+        "d/dx tan x = (sec(x))^2 = 2/(cos(2x) + 1)"
+        x, = f.operands()
+        return (2.0/(cos(2.0*x) + 1.0),)
+
+    def acos(self, f):
+        "d/dx acos x = -1/sqrt(1 - x^2)"
+        x, = f.operands()
+        return (-1.0/sqrt(1.0 - x**2),)
+    
+    def asin(self, f):
+        "d/dx asin x = 1/sqrt(1 - x^2)"
+        x, = f.operands()
+        return (1.0/sqrt(1.0 - x**2),)
+
+    def atan(self, f):
+        "d/dx atan x = 1/(1 + x^2)"
+        x, = f.operands()
+        return (1.0/(1.0 + x**2),)

=== modified file 'ufl/algorithms/ufl2latex.py'
--- ufl/algorithms/ufl2latex.py	2010-02-07 21:15:52 +0000
+++ ufl/algorithms/ufl2latex.py	2010-03-17 15:16:37 +0000
@@ -22,7 +22,7 @@
 from ufl.algebra import Sum, Product, Division, Power, Abs
 from ufl.indexsum import IndexSum
 from ufl.tensoralgebra import Transposed, Outer, Inner, Dot, Cross, Trace, Determinant, Inverse, Deviatoric, Cofactor
-from ufl.mathfunctions import Sqrt, Exp, Ln, Cos, Sin
+from ufl.mathfunctions import Sqrt, Exp, Ln, Cos, Sin, Tan, Acos, Asin, Atan
 from ufl.restriction import PositiveRestricted, NegativeRestricted
 from ufl.differentiation import SpatialDerivative, VariableDerivative, Grad, Div, Curl
 from ufl.conditional import EQ, NE, LE, GE, LT, GT, Conditional
@@ -62,7 +62,7 @@
                             Determinant, Trace, Cofactor, Inverse, Deviatoric))
     precedence_list.append((Product, Division, Cross, Dot, Outer, Inner))
     precedence_list.append((Indexed, Transposed, Power))
-    precedence_list.append((Abs, Cos, Exp, Ln, Sin, Sqrt))
+    precedence_list.append((Abs, Cos, Exp, Ln, Sin, Sqrt, Tan, Acos, Asin, Atan))
     precedence_list.append((Variable,))
     precedence_list.append(terminal_classes)
 
@@ -206,6 +206,18 @@
     def sin(self, o, f):
 
+    def tan(self, o, f):
+        return r"\tan{%s}" % par(f)
+
+    def acos(self, o, f):
+        return r"\arccos{%s}" % par(f)
+
+    def asin(self, o, f):
+        return r"\arcsin{%s}" % par(f)
+
+    def atan(self, o, f):
+        return r"\arctan{%s}" % par(f)
+
     def power(self, o, a, b):
         return "{%s}^{%s}" % (par(a), par(b))

+++ ufl/classes.py	2010-03-17 15:16:37 +0000
+from ufl.mathfunctions import MathFunction, Sqrt, Exp, Ln, Cos, Sin, Tan, Acos, Asin, Atan
@@ -142,7 +142,7 @@
 
     # If parent operator binds stronger than child, must parenthesize child
 
-    precedence_list.append((Abs, MathFunction,)) # Abs, Sqrt, Exp, Ln, Cos, Sin
+    precedence_list.append((Abs, MathFunction,)) # Abs, Sqrt, Exp, Ln, Cos, Sin, Tan, Acos, Asin, Atan
     precedence_list.append((Variable,))
 
     precedence_list.append((Terminal,)) # terminal_classes

+++ ufl/mathfunctions.py	2010-03-17 15:16:37 +0000
@@ -94,3 +94,38 @@
 
+class Tan(MathFunction):
+    def __new__(cls, argument):
+        if isinstance(argument, (ScalarValue, Zero)):
+            return FloatValue(math.tan(float(argument)))
+        return MathFunction.__new__(cls)
+    
+    def __init__(self, argument):
+        MathFunction.__init__(self, "tan", argument)
+
+class Acos(MathFunction):
+    def __new__(cls, argument):
+        if isinstance(argument, (ScalarValue, Zero)):
+            return FloatValue(math.acos(float(argument)))
+        return MathFunction.__new__(cls)
+    
+    def __init__(self, argument):
+        MathFunction.__init__(self, "acos", argument)
+
+class Asin(MathFunction):
+    def __new__(cls, argument):
+        if isinstance(argument, (ScalarValue, Zero)):
+            return FloatValue(math.asin(float(argument)))
+        return MathFunction.__new__(cls)
+    
+    def __init__(self, argument):
+        MathFunction.__init__(self, "asin", argument)
+
+class Atan(MathFunction):
+    def __new__(cls, argument):
+        if isinstance(argument, (ScalarValue, Zero)):
+            return FloatValue(math.atan(float(argument)))
+        return MathFunction.__new__(cls)
+    
+    def __init__(self, argument):
+        MathFunction.__init__(self, "atan", argument)

+++ ufl/operators.py	2010-03-17 15:16:37 +0000
+from ufl.mathfunctions import Sqrt, Exp, Ln, Cos, Sin, Tan, Acos, Asin, Atan
 
@@ -283,3 +283,18 @@
     "The sinus of f."
 
+def tan(f):
+    "The tangent of f."
+    return _mathfunction(f, Tan, math.tan)
+
+def acos(f):
+    "The inverse cosinus of f."
+    return _mathfunction(f, Acos, math.acos)
+
+def asin(f):
+    "The inverse sinus of f."
+    return _mathfunction(f, Asin, math.asin)
+
+def atan(f):
+    "The inverse tangent of f."
+    return _mathfunction(f, Atan, math.atan)

# Begin bundle
# Bazaar merge directive format 2 (Bazaar 0.90)
# revision_id: [email-masked]
# target_branch: bzr+ssh://bazaar.launchpad.net/~ffc-core/ffc/main/
# testament_sha1: 4e20dcd6dea2c8ed304c1261b679f51ff58cee1a
# timestamp: 2010-03-17 16:21:18 +0100
# base_revision_id: [email-masked]\
#   viogrlmwwjlxdifp
# 
# Begin patch
=== modified file 'ffc/cpp.py'
--- ffc/cpp.py	2010-03-11 13:59:32 +0000
+++ ffc/cpp.py	2010-03-17 15:21:01 +0000
@@ -69,6 +69,10 @@
     "ln":             lambda v: "std::log(%s)" % str(v),
     "cos":            lambda v: "std::cos(%s)" % str(v),
     "sin":            lambda v: "std::sin(%s)" % str(v),
+    "tan":            lambda v: "std::tan(%s)" % str(v),
+    "acos":           lambda v: "std::acos(%s)" % str(v),
+    "asin":           lambda v: "std::asin(%s)" % str(v),
+    "atan":           lambda v: "std::atan(%s)" % str(v),
     "absolute value": lambda v: "std::abs(%s)" % str(v),
     "sqrt":           lambda v: "std::sqrt(%s)" % str(v),
     "addition":       lambda v: _add(v),

=== modified file 'ffc/quadrature/quadraturetransformerbase.py'
--- ffc/quadrature/quadraturetransformerbase.py	2010-03-11 13:59:32 +0000
+++ ffc/quadrature/quadraturetransformerbase.py	2010-03-17 15:21:01 +0000
@@ -541,6 +541,22 @@
         #print("\n\nVisiting Sin: " + repr(o) + "with operands: " + "\n".join(map(repr,operands)))
         return self._math_function(operands, format["sin"])
 
+    def tan(self, o, *operands):
+        #print("\n\nVisiting Tan: " + repr(o) + "with operands: " + "\n".join(map(repr,operands)))
+        return self._math_function(operands, format["tan"])
+
+    def acos(self, o, *operands):
+        #print("\n\nVisiting Acos: " + repr(o) + "with operands: " + "\n".join(map(repr,operands)))
+        return self._math_function(operands, format["acos"])
+
+    def asin(self, o, *operands):
+        #print("\n\nVisiting Asin: " + repr(o) + "with operands: " + "\n".join(map(repr,operands)))
+        return self._math_function(operands, format["asin"])
+
+    def atan(self, o, *operands):
+        #print("\n\nVisiting Atan: " + repr(o) + "with operands: " + "\n".join(map(repr,operands)))
+        return self._math_function(operands, format["atan"])
+
     # -------------------------------------------------------------------------
     # PositiveRestricted and NegativeRestricted (restriction.py).
     # -------------------------------------------------------------------------

# Begin bundle

---

From: Kristian Oelgaard <[email-masked]>
Date: Wed, 17 Mar 2010 17:36:44 +0100 (CET)

> Also attached is a small FFC patch to expose this functionality.

Applied and pushed.

Kristian