0

I am working on a project using PuLP and I am trying to create a terminal prompt to allow users to input their data and then my Python program will change the input to code readable by PuLP. To do this I must allow users to input:

2*a + 3*b <= c

and my code will eval() this code as well as creating variables a, b and c such

a = LpVariable("a",None,None,LpContinuous)
b = LpVariable("b",None,None,LpContinuous)
c = LpVariable("c",None,None,LpContinuous)

any ideas? I've tried using exec() but it does not seem to like this much.

at the moment I'm getting the input via:

print "Please enter your constraints 1 at a time and enter '#' when done:"
control = True
while (control):
    entry = raw_input('-->')
    entryS = ""
    entryS += entry

so the string 2*a+3*B <= c is stored as entryS

10
  • 1
    how are you getting the input from the terminal now? Commented Dec 6, 2012 at 20:30
  • i would first input the whole thing as string, then you have to check each "letter" to see if it's a char, int oder operator and then declare a matching variable. Commented Dec 6, 2012 at 20:35
  • @usmcs updated the question, sorry. Commented Dec 6, 2012 at 20:39
  • That will delete enstryS every time. Commented Dec 6, 2012 at 20:41
  • @user1880863 how do I declare a matching variable if I don't know what is going to be said it could be 2*bla + 3*anystring <= jkl Commented Dec 6, 2012 at 20:41

2 Answers 2

1

Using eval() might not be such a good idea, but if you insist (Python 3):

call = lambda f: lambda *args: f(*args)
flip = lambda f: lambda *args: f(*reversed(args))


class Expression:

    def __repr__(self):
        return '{}({})'.format(type(self).__name__, self)


class BinaryExpression(Expression):

    def __init__(self, left, right):
        self.left = promote(left)
        self.right = promote(right)

    def __str__(self):
        return '({} {} {})'.format(self.op, self.left, self.right)


class Variable(Expression):

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

    def __str__(self):
        return self.name


class Number(Expression):

    def __init__(self, value):
        self.value = int(value)

    def __str__(self):
        return str(self.value)


class Multiplication(BinaryExpression):
    op = '*'


class Addition(BinaryExpression):
    op = '+'


class Smaller(BinaryExpression):
    op = '<'


class Greater(BinaryExpression):
    op = '>'


class SmallerOrEqual(BinaryExpression):
    op = '<='


class GreaterOrEqual(BinaryExpression):
    op = '>='


Expression.__mul__ = call(Multiplication)
Expression.__rmul__ = flip(Multiplication)
Expression.__add__ = call(Addition)
Expression.__radd__ = flip(Addition)
Expression.__lt__ = call(Smaller)
Expression.__gt__ = call(Greater)
Expression.__le__ = call(SmallerOrEqual)
Expression.__ge__ = call(GreaterOrEqual)


def promote(item):
    if isinstance(item, str):
        return Variable(item)
    elif isinstance(item, int):
        return Number(item)
    else:
        return item


class LpVariable:

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

    def __str__(self):
        return 'LpVariable({}, {}, {}, {})'.format(
            self.name,
            self.x,
            self.y,
            self.z,
        )

    __repr__ = __str__


LpContinuous = 'LpContinuous'


class ExpressionVisitor:

    def visit(self, node):
        return getattr(self, 'visit_' + type(node).__name__)(node)


class LpTransformer(ExpressionVisitor):

    def visit_Variable(self, node):
        return LpVariable(node.name, None, None, LpContinuous)

    def visit_Number(self, node):
        return node.value

    def visit_Multiplication(self, node):
        return [node.op, self.visit(node.left), self.visit(node.right)]

    def visit_Addition(self, node):
        return [node.op, self.visit(node.left), self.visit(node.right)]

    def visit_Smaller(self, node):
        return [node.op, self.visit(node.left), self.visit(node.right)]

    def visit_Greater(self, node):
        return [node.op, self.visit(node.left), self.visit(node.right)]

    def visit_SmallerOrEqual(self, node):
        return [node.op, self.visit(node.left), self.visit(node.right)]

    def visit_GreaterOrEqual(self, node):
        return [node.op, self.visit(node.left), self.visit(node.right)]


class Evaluator(ExpressionVisitor):

    def __init__(self, **env):
        self.env = env

    def visit_Variable(self, node):
        return self.env[node.name]

    def visit_Number(self, node):
        return node.value

    def visit_Multiplication(self, node):
        return self.visit(node.left) * self.visit(node.right)

    def visit_Addition(self, node):
        return self.visit(node.left) + self.visit(node.right)

    def visit_Smaller(self, node):
        return self.visit(node.left) < self.visit(node.right)

    def visit_Greater(self, node):
        return self.visit(node.left) > self.visit(node.right)

    def visit_SmallerOrEqual(self, node):
        return self.visit(node.left) <= self.visit(node.right)

    def visit_GreaterOrEqual(self, node):
        return self.visit(node.left) >= self.visit(node.right)


class Namespace(dict):

    def __missing__(self, key):
        value = self[key] = Variable(key)
        return value


def main():
    constraints = '2*a + 3*b <= c'
    namespace = Namespace()
    tree = eval(constraints, {}, namespace)
    print('AST in prefix notation:', tree)
    print()
    print('Namespace:', namespace)
    print()
    print('LP-Transformed tree:')
    import pprint
    pprint.pprint(LpTransformer().visit(tree))
    print()
    print('Evaluated with a=3, b=5, c=10:')
    pprint.pprint(Evaluator(a=3, b=5, c=10).visit(tree))
    print()
    print('Evaluated with a=3, b=5, c=100:')
    pprint.pprint(Evaluator(a=3, b=5, c=100).visit(tree))


if __name__ == '__main__':
    main()

Result:

AST in prefix notation: (<= (+ (* 2 a) (* 3 b)) c)    

Namespace: {'a': Variable(a), 'c': Variable(c), 'b': Variable(b)}

LP-Transformed tree:
['<=',
 ['+',
  ['*', 2, LpVariable(a, None, None, LpContinuous)],
  ['*', 3, LpVariable(b, None, None, LpContinuous)]],
 LpVariable(c, None, None, LpContinuous)]

Evaluated with a=3, b=5, c=10:
False

Evaluated with a=3, b=5, c=100:
True

The LpVariable class is obviously a mockup. Also, the LpTransformer class should produce something that's usable by pulp. Just change the visit_* methods accordingly.

Numbers are all ints, which you might not want. You should probably add floats and/or convert all numbers to decimal.Decimal.

Instead of using eval() I'd probably write a real parser, maybe with pyparsing, or, my favorite for stuff like that, Parcon.

Sign up to request clarification or add additional context in comments.

Comments

0

Put

entryS = "" 

before the while loop.

1 Comment

The user is going to input a set of constraints for PuLP (a linear programming package for python) one at a time. However, PuLP needs you to declare the variables seperately so I want to iterate through the input to try find the variables and put them into the second type of code. Sorry if I'm not explaining very well, I'm new to this. so entryS has to be reset for each input.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.