def constrain(self, par, con, ns={}): """Constrain a parameter to an equation. Note that only one constraint can exist on a Parameter at a time. par -- The name of a Parameter or a Parameter to constrain. con -- A string representation of the constraint equation or a Parameter to constrain to. A constraint equation must consist of numpy operators and "known" Parameters. Parameters are known if they are in the ns argument, or if they are managed by this object. ns -- A dictionary of Parameters, indexed by name, that are used in the parameter, but not part of this object (default {}). Raises ValueError if ns uses a name that is already used for a variable. Raises ValueError if par is a string but not part of this object or in ns. Raises ValueError if par is marked as constant. """ if isinstance(par, basestring): name = par par = self.get(name) if par is None: par = ns.get(name) if par is None: raise ValueError("The parameter cannot be found") if par.const: raise ValueError("The parameter '%s' is constant" % par) if isinstance(con, basestring): eqstr = con eq = equationFromString(con, self._eqfactory, ns) else: eq = Equation(root=con) eqstr = con.name eq.name = "_constraint_%s" % par.name # Make and store the constraint con = Constraint() con.constrain(par, eq) # Store the equation string so it can be shown later. con.eqstr = eqstr self._constraints[par] = con # Our configuration changed self._updateConfiguration() return
def restrain(self, res, lb=-inf, ub=inf, sig=1, scaled=False, ns={}): """Restrain an expression to specified bounds res -- An equation string or Parameter to restrain. lb -- The lower bound on the restraint evaluation (default -inf). ub -- The lower bound on the restraint evaluation (default inf). sig -- The uncertainty on the bounds (default 1). scaled -- A flag indicating if the restraint is scaled (multiplied) by the unrestrained point-average chi^2 (chi^2/numpoints) (default False). ns -- A dictionary of Parameters, indexed by name, that are used in the equation string, but not part of the RecipeOrganizer (default {}). The penalty is calculated as (max(0, lb - val, val - ub)/sig)**2 and val is the value of the calculated equation. This is multipled by the average chi^2 if scaled is True. Raises ValueError if ns uses a name that is already used for a Parameter. Raises ValueError if res depends on a Parameter that is not part of the RecipeOrganizer and that is not defined in ns. Returns the Restraint object for use with the 'unrestrain' method. """ if isinstance(res, basestring): eqstr = res eq = equationFromString(res, self._eqfactory, ns) else: eq = Equation(root=res) eqstr = res.name # Make and store the restraint res = Restraint(eq, lb, ub, sig, scaled) res.eqstr = eqstr self.addRestraint(res) return res
def testSimpleFunction(self): """Test a simple function.""" # Make some variables v1, v2, v3, v4, c = _makeArgs(5) c.name = "c" c.const = True # Make some operations mult = literals.MultiplicationOperator() root = mult2 = literals.MultiplicationOperator() plus = literals.AdditionOperator() minus = literals.SubtractionOperator() # Create the equation c*(v1+v3)*(v4-v2) plus.addLiteral(v1) plus.addLiteral(v3) minus.addLiteral(v4) minus.addLiteral(v2) mult.addLiteral(plus) mult.addLiteral(minus) mult2.addLiteral(mult) mult2.addLiteral(c) # Set the values of the variables. # The equation should evaluate to 2.5*(1+3)*(4-2) = 20 v1.setValue(1) v2.setValue(2) v3.setValue(3) v4.setValue(4) c.setValue(2.5) # Make an equation and test eq = Equation("eq", mult2) self.assertTrue(eq._value is None) args = eq.args self.assertTrue(v1 in args) self.assertTrue(v2 in args) self.assertTrue(v3 in args) self.assertTrue(v4 in args) self.assertTrue(c not in args) self.assertTrue(root is eq.root) self.assertTrue(v1 is eq.v1) self.assertTrue(v2 is eq.v2) self.assertTrue(v3 is eq.v3) self.assertTrue(v4 is eq.v4) self.assertEqual(20, eq()) # 20 = 2.5*(1+3)*(4-2) self.assertEqual(20, eq.getValue()) # same as above self.assertEqual(20, eq.value) # same as above self.assertEqual(25, eq(v1=2)) # 25 = 2.5*(2+3)*(4-2) self.assertEqual(50, eq(v2=0)) # 50 = 2.5*(2+3)*(4-0) self.assertEqual(30, eq(v3=1)) # 30 = 2.5*(2+1)*(4-0) self.assertEqual(0, eq(v4=0)) # 20 = 2.5*(2+1)*(0-0) # Try some swapping eq.swap(v4, v1) self.assertTrue(eq._value is None) self.assertEqual(15, eq()) # 15 = 2.5*(2+1)*(2-0) args = eq.args self.assertTrue(v4 not in args) # Try to create a dependency loop self.assertRaises(ValueError, eq.swap, v1, eq.root) self.assertRaises(ValueError, eq.swap, v1, plus) self.assertRaises(ValueError, eq.swap, v1, minus) self.assertRaises(ValueError, eq.swap, v1, mult) self.assertRaises(ValueError, eq.swap, v1, root) # Swap the root eq.swap(eq.root, v1) self.assertTrue(eq._value is None) self.assertEqual(v1.value, eq()) self.assertTrue(noObserversInGlobalBuilders()) return
def testSimpleFunction(self): """Test a simple function.""" # Make some variables v1, v2, v3, v4, c = _makeArgs(5) c.name = "c" c.const = True # Make some operations mult = literals.MultiplicationOperator() root = mult2 = literals.MultiplicationOperator() plus = literals.AdditionOperator() minus = literals.SubtractionOperator() # Create the equation c*(v1+v3)*(v4-v2) plus.addLiteral(v1) plus.addLiteral(v3) minus.addLiteral(v4) minus.addLiteral(v2) mult.addLiteral(plus) mult.addLiteral(minus) mult2.addLiteral(mult) mult2.addLiteral(c) # Set the values of the variables. # The equation should evaluate to 2.5*(1+3)*(4-2) = 20 v1.setValue(1) v2.setValue(2) v3.setValue(3) v4.setValue(4) c.setValue(2.5) # Make an equation and test eq = Equation("eq", mult2) self.assertTrue(eq._value is None) args = eq.args self.assertTrue(v1 in args) self.assertTrue(v2 in args) self.assertTrue(v3 in args) self.assertTrue(v4 in args) self.assertTrue(c not in args) self.assertTrue(root is eq.root) self.assertTrue(v1 is eq.v1) self.assertTrue(v2 is eq.v2) self.assertTrue(v3 is eq.v3) self.assertTrue(v4 is eq.v4) self.assertEqual(20, eq()) # 20 = 2.5*(1+3)*(4-2) self.assertEqual(20, eq.getValue()) # same as above self.assertEqual(20, eq.value) # same as above self.assertEqual(25, eq(v1=2)) # 25 = 2.5*(2+3)*(4-2) self.assertEqual(50, eq(v2=0)) # 50 = 2.5*(2+3)*(4-0) self.assertEqual(30, eq(v3=1)) # 30 = 2.5*(2+1)*(4-0) self.assertEqual(0, eq(v4=0)) # 20 = 2.5*(2+1)*(0-0) # Try some swapping eq.swap(v4, v1) self.assertTrue(eq._value is None) self.assertEqual(15, eq()) # 15 = 2.5*(2+1)*(2-0) args = eq.args self.assertTrue(v4 not in args) # Try to create a dependency loop self.assertRaises(ValueError, eq.swap, v1, eq.root) self.assertRaises(ValueError, eq.swap, v1, plus) self.assertRaises(ValueError, eq.swap, v1, minus) self.assertRaises(ValueError, eq.swap, v1, mult) self.assertRaises(ValueError, eq.swap, v1, root) # Swap the root eq.swap(eq.root, v1) self.assertTrue(eq._value is None) self.assertEqual(v1.value, eq()) return