1
+ # Section 3: Algebraic simplification
2
+
3
+ # This code implements a simple computer algebra system, which takes in an
4
+ # expression made of nested sums and products, and simplifies it into a
5
+ # single sum of products. The goal is described in more detail in the
6
+ # problem set writeup.
7
+
8
+ # Much of this code is already implemented. We provide you with a
9
+ # representation for sums and products, and a top-level simplify() function
10
+ # which applies the associative law in obvious cases. For example, it
11
+ # turns both (a + (b + c)) and ((a + b) + c) into the simpler expression
12
+ # (a + b + c).
13
+
14
+ # However, the code has a gap in it: it cannot simplify expressions that are
15
+ # multiplied together. In interesting cases of this, you will need to apply
16
+ # the distributive law.
17
+
18
+ # Your goal is to fill in the do_multiply() function so that multiplication
19
+ # can be simplified as intended.
20
+
21
+ # Testing will be mathematical: If you return a flat list that
22
+ # evaluates to the same value as the original expression, you will
23
+ # get full credit.
24
+
25
+
26
+ # We've already defined the data structures that you'll use to symbolically
27
+ # represent these expressions, as two classes called Sum and Product,
28
+ # defined below. These classes both descend from the abstract Expression class.
29
+ #
30
+ # The top level function that will be called is the .simplify() method of an
31
+ # Expression.
32
+ #
33
+ # >>> expr = Sum([1, Sum([2, 3])])
34
+ # >>> expr.simplify()
35
+ # Sum([1, 2, 3])
36
+
37
+
38
+ ### Expression classes _____________________________________________________
39
+
40
+ # Expressions will be represented as "Sum()" and "Product()" objects.
41
+ # These objects can be treated just like lists (they inherit from the
42
+ # "list" class), but you can test for their type using the "isinstance()"
43
+ # function. For example:
44
+ #
45
+ # >>> isinstance(Sum([1,2,3]), Sum)
46
+ # True
47
+ # >>> isinstance(Product([1,2,3]), Product)
48
+ # True
49
+ # >>> isinstance(Sum([1,2,3]), Expression) # Sums and Products are both Expressions
50
+ # True
51
+
52
+ class Expression :
53
+ "This abstract class does nothing on its own."
54
+ pass
55
+
56
+ class Sum (list , Expression ):
57
+ """
58
+ A Sum acts just like a list in almost all regards, except that this code
59
+ can tell it is a Sum using isinstance(), and we add useful methods
60
+ such as simplify().
61
+
62
+ Because of this:
63
+ * You can index into a sum like a list, as in term = sum[0].
64
+ * You can iterate over a sum with "for term in sum:".
65
+ * You can convert a sum to an ordinary list with the list() constructor:
66
+ the_list = list(the_sum)
67
+ * You can convert an ordinary list to a sum with the Sum() constructor:
68
+ the_sum = Sum(the_list)
69
+ """
70
+ def __repr__ (self ):
71
+ return "Sum(%s)" % list .__repr__ (self )
72
+
73
+ def simplify (self ):
74
+ """
75
+ This is the starting point for the task you need to perform. It
76
+ removes unnecessary nesting and applies the associative law.
77
+ """
78
+ terms = self .flatten ()
79
+ if len (terms ) == 1 :
80
+ return simplify_if_possible (terms [0 ])
81
+ else :
82
+ return Sum ([simplify_if_possible (term ) for term in terms ]).flatten ()
83
+
84
+ def flatten (self ):
85
+ """Simplifies nested sums."""
86
+ terms = []
87
+ for term in self :
88
+ if isinstance (term , Sum ):
89
+ terms += list (term )
90
+ else :
91
+ terms .append (term )
92
+ return Sum (terms )
93
+
94
+
95
+ class Product (list , Expression ):
96
+ """
97
+ See the documentation above for Sum. A Product acts almost exactly
98
+ like a list, and can be converted to and from a list when necessary.
99
+ """
100
+ def __repr__ (self ):
101
+ return "Product(%s)" % list .__repr__ (self )
102
+
103
+ def simplify (self ):
104
+ """
105
+ To simplify a product, we need to multiply all its factors together
106
+ while taking things like the distributive law into account. This
107
+ method calls multiply() repeatedly, leading to the code you will
108
+ need to write.
109
+ """
110
+ factors = []
111
+ for factor in self :
112
+ if isinstance (factor , Product ):
113
+ factors += list (factor )
114
+ else :
115
+ factors .append (factor )
116
+ result = Product ([1 ])
117
+ for factor in factors :
118
+ result = multiply (result , simplify_if_possible (factor ))
119
+ return result .flatten ()
120
+
121
+ def flatten (self ):
122
+ """Simplifies nested products."""
123
+ factors = []
124
+ for factor in self :
125
+ if isinstance (factor , Product ):
126
+ factors += list (factor )
127
+ else :
128
+ factors .append (factor )
129
+ return Product (factors )
130
+
131
+ def simplify_if_possible (expr ):
132
+ """
133
+ A helper function that guards against trying to simplify a non-Expression.
134
+ """
135
+ if isinstance (expr , Expression ):
136
+ return expr .simplify ()
137
+ else :
138
+ return expr
139
+
140
+ # You may find the following helper functions to be useful.
141
+ # "multiply" is provided for you; but you will need to write "do_multiply"
142
+ # if you would like to use it.
143
+
144
+ def multiply (expr1 , expr2 ):
145
+ """
146
+ This function makes sure that its arguments are represented as either a
147
+ Sum or a Product, and then passes the hard work onto do_multiply.
148
+ """
149
+ # Simple expressions that are not sums or products can be handled
150
+ # in exactly the same way as products -- they just have one thing in them.
151
+ if not isinstance (expr1 , Expression ): expr1 = Product ([expr1 ])
152
+ if not isinstance (expr2 , Expression ): expr2 = Product ([expr2 ])
153
+ return do_multiply (expr1 , expr2 )
154
+
155
+
156
+ def do_multiply (expr1 , expr2 ):
157
+ """
158
+ You have two Expressions, and you need to make a simplified expression
159
+ representing their product. They are guaranteed to be of type Expression
160
+ -- that is, either Sums or Products -- by the multiply() function that
161
+ calls this one.
162
+
163
+ So, you have four cases to deal with:
164
+ * expr1 is a Sum, and expr2 is a Sum
165
+ * expr1 is a Sum, and expr2 is a Product
166
+ * expr1 is a Product, and expr2 is a Sum
167
+ * expr1 is a Product, and expr2 is a Product
168
+
169
+ You need to create Sums or Products that represent what you get by
170
+ applying the algebraic rules of multiplication to these expressions,
171
+ and simplifying.
172
+
173
+ Look above for details on the Sum and Product classes. The Python operator
174
+ '*' will not help you.
175
+ """
176
+
177
+ if isinstance (expr1 , Sum ) and isinstance (expr2 , Sum ):
178
+ sm = []
179
+ for term1 in expr1 :
180
+ for term2 in expr2 :
181
+ prd = []
182
+ prd .append (term1 )
183
+ prd .append (term2 )
184
+ sum_list .append (Product (prd ))
185
+ elif isinstance (expr1 , Product ) and isinstance (expr2 , Sum ):
186
+ sm = []
187
+ for term2 in expr2 :
188
+ prd = [term2 ]
189
+ prd .extend (list (expr1 ))
190
+ sm .append (Product (prd ))
191
+ return Sum (sm )
192
+ elif isinstance (expr1 , Sum ) and isinstance (expr2 , Product ):
193
+ sm = []
194
+ for term1 in expr1 :
195
+ prd = [term1 ]
196
+ prd .extend (list (expr2 ))
197
+ sm .append (Product (prd ))
198
+ return Sum (sm )
199
+ else :
200
+ prd = list (expr1 )
201
+ prd .extend (list (expr2 ))
202
+ return Product (prd )
0 commit comments