OASIS
Open Algebra Software
Loading...
Searching...
No Matches
BoundedBinaryExpression.hpp
Go to the documentation of this file.
1//
2// Created by Matthew McCall on 5/2/24.
3//
4
5#ifndef OASIS_BOUNDEDBINARYEXPRESSION_HPP
6#define OASIS_BOUNDEDBINARYEXPRESSION_HPP
7
9#include "Expression.hpp"
10#include "RecursiveCast.hpp"
11#include "Visit.hpp"
12
13namespace Oasis {
24template <template <IExpression, IExpression, IExpression, IExpression> class DerivedT, IExpression MostSigOpT = Expression, IExpression LeastSigOpT = MostSigOpT, IExpression LowerBoundT = Expression, IExpression UpperBoundT = LowerBoundT>
25class BoundedBinaryExpression : public BoundedExpression<LowerBoundT, UpperBoundT> {
26
27 using DerivedSpecialized = DerivedT<MostSigOpT, LeastSigOpT, LowerBoundT, UpperBoundT>;
28 using DerivedGeneralized = DerivedT<Expression, Expression, Expression, Expression>;
29
30public:
33 {
34 if (other.HasMostSigOp()) {
36 }
37
38 if (other.HasLeastSigOp()) {
40 }
41 }
42
43 BoundedBinaryExpression(const MostSigOpT& mostSigOp, const LeastSigOpT& leastSigOp)
44 {
45 SetMostSigOp(mostSigOp);
46 SetLeastSigOp(leastSigOp);
47 }
48
49 [[nodiscard]] auto Copy() const -> std::unique_ptr<Expression> final
50 {
51 return std::make_unique<DerivedSpecialized>(*static_cast<const DerivedSpecialized*>(this));
52 }
53
54 auto Copy(tf::Subflow& subflow) const -> std::unique_ptr<Expression> final
55 {
56 DerivedSpecialized copy;
57
58 if (this->mostSigOp) {
59 subflow.emplace([this, &copy](tf::Subflow& sbf) {
60 copy.SetMostSigOp(mostSigOp->Copy(sbf), sbf);
61 });
62 }
63
64 if (this->leastSigOp) {
65 subflow.emplace([this, &copy](tf::Subflow& sbf) {
66 copy.SetLeastSigOp(leastSigOp->Copy(sbf), sbf);
67 });
68 }
69
70 subflow.join();
71
73 }
74 [[nodiscard]] auto Differentiate(const Expression& differentiationVariable) const -> std::unique_ptr<Expression> override
75 {
76 return Generalize()->Differentiate(differentiationVariable);
77 }
78 [[nodiscard]] auto Equals(const Expression& other) const -> bool final
79 {
80 if (this->GetType() != other.GetType()) {
81 return false;
82 }
83
84 const auto otherGeneralized = other.Generalize();
85 const auto& otherBinaryGeneralized = static_cast<const DerivedGeneralized&>(*otherGeneralized);
86
87 bool mostSigOpMismatch = false, leastSigOpMismatch = false;
88
89 if (this->HasMostSigOp() == otherBinaryGeneralized.HasMostSigOp()) {
90 if (mostSigOp && otherBinaryGeneralized.HasMostSigOp()) {
91 mostSigOpMismatch = !mostSigOp->Equals(otherBinaryGeneralized.GetMostSigOp());
92 }
93 } else {
94 mostSigOpMismatch = true;
95 }
96
97 if (this->HasLeastSigOp() == otherBinaryGeneralized.HasLeastSigOp()) {
98 if (this->HasLeastSigOp() && otherBinaryGeneralized.HasLeastSigOp()) {
99 leastSigOpMismatch = !leastSigOp->Equals(otherBinaryGeneralized.GetLeastSigOp());
100 }
101 } else {
102 mostSigOpMismatch = true;
103 }
104
105 if (!mostSigOpMismatch && !leastSigOpMismatch) {
106 return true;
107 }
108
109 if (!(this->GetCategory() & Associative)) {
110 return false;
111 }
112
113 auto thisFlattened = std::vector<std::unique_ptr<Expression>> {};
114 auto otherFlattened = std::vector<std::unique_ptr<Expression>> {};
115
116 this->Flatten(thisFlattened);
117 otherBinaryGeneralized.Flatten(otherFlattened);
118
119 for (const auto& thisOperand : thisFlattened) {
120 if (std::find_if(otherFlattened.begin(), otherFlattened.end(), [&thisOperand](const auto& otherOperand) {
121 return thisOperand->Equals(*otherOperand);
122 })
123 == otherFlattened.end()) {
124 return false;
125 }
126 }
127
128 return true;
129 }
130
131 [[nodiscard]] auto Generalize() const -> std::unique_ptr<Expression> final
132 {
133 DerivedGeneralized generalized;
134
135 if (this->mostSigOp) {
136 generalized.SetMostSigOp(*this->mostSigOp->Copy());
137 }
138
139 if (this->leastSigOp) {
140 generalized.SetLeastSigOp(*this->leastSigOp->Copy());
141 }
142
143 return std::make_unique<DerivedGeneralized>(generalized);
144 }
145
146 auto Generalize(tf::Subflow& subflow) const -> std::unique_ptr<Expression> final
147 {
148 DerivedGeneralized generalized;
149
150 if (this->mostSigOp) {
151 subflow.emplace([this, &generalized](tf::Subflow& sbf) {
152 generalized.SetMostSigOp(*this->mostSigOp->Copy(sbf));
153 });
154 }
155
156 if (this->leastSigOp) {
157 subflow.emplace([this, &generalized](tf::Subflow& sbf) {
158 generalized.SetLeastSigOp(*this->leastSigOp->Copy(sbf));
159 });
160 }
161
162 subflow.join();
163
164 return std::make_unique<DerivedGeneralized>(generalized);
165 }
166
167 auto Simplify(tf::Subflow& subflow) const -> std::unique_ptr<Expression> override
168 {
169 std::unique_ptr<Expression> generalized, simplified;
170
171 tf::Task generalizeTask = subflow.emplace([this, &generalized](tf::Subflow& sbf) {
172 generalized = Generalize(sbf);
173 });
174
175 tf::Task simplifyTask = subflow.emplace([&generalized, &simplified](tf::Subflow& sbf) {
176 simplified = generalized->Simplify(sbf);
177 });
178
179 simplifyTask.succeed(generalizeTask);
180 subflow.join();
181
182 return simplified;
183 }
184
185 [[nodiscard]] auto StructurallyEquivalent(const Expression& other) const -> bool final
186 {
187 if (this->GetType() != other.GetType()) {
188 return false;
189 }
190
191 const std::unique_ptr<Expression> otherGeneralized = other.Generalize();
192 const auto& otherBinaryGeneralized = static_cast<const DerivedGeneralized&>(*otherGeneralized);
193
194 if (this->HasMostSigOp() == otherBinaryGeneralized.HasMostSigOp()) {
195 if (this->HasMostSigOp() && otherBinaryGeneralized.HasMostSigOp()) {
196 if (!mostSigOp->StructurallyEquivalent(otherBinaryGeneralized.GetMostSigOp())) {
197 return false;
198 }
199 }
200 }
201
202 if (this->HasLeastSigOp() == otherBinaryGeneralized.HasLeastSigOp()) {
203 if (this->HasLeastSigOp() && otherBinaryGeneralized.HasLeastSigOp()) {
204 if (!leastSigOp->StructurallyEquivalent(otherBinaryGeneralized.GetLeastSigOp())) {
205 return false;
206 }
207 }
208 }
209
210 return true;
211 }
212
213 auto StructurallyEquivalent(const Expression& other, tf::Subflow& subflow) const -> bool final
214 {
215 if (this->GetType() != other.GetType()) {
216 return false;
217 }
218
219 std::unique_ptr<Expression> otherGeneralized;
220
221 tf::Task generalizeTask = subflow.emplace([&](tf::Subflow& sbf) {
222 otherGeneralized = other.Generalize(sbf);
223 });
224
225 bool mostSigOpEquivalent = false, leastSigOpEquivalent = false;
226
227 if (this->mostSigOp) {
228 tf::Task compMostSigOp = subflow.emplace([this, &otherGeneralized, &mostSigOpEquivalent](tf::Subflow& sbf) {
229 if (const auto& otherBinary = static_cast<const DerivedGeneralized&>(*otherGeneralized); otherBinary.HasMostSigOp()) {
230 mostSigOpEquivalent = mostSigOp->StructurallyEquivalent(otherBinary.GetMostSigOp(), sbf);
231 }
232 });
233
234 compMostSigOp.succeed(generalizeTask);
235 }
236
237 if (this->leastSigOp) {
238 tf::Task compLeastSigOp = subflow.emplace([this, &otherGeneralized, &leastSigOpEquivalent](tf::Subflow& sbf) {
239 if (const auto& otherBinary = static_cast<const DerivedGeneralized&>(*otherGeneralized); otherBinary.HasLeastSigOp()) {
240 leastSigOpEquivalent = leastSigOp->StructurallyEquivalent(otherBinary.GetLeastSigOp(), sbf);
241 }
242 });
243
244 compLeastSigOp.succeed(generalizeTask);
245 }
246
247 subflow.join();
248
249 return mostSigOpEquivalent && leastSigOpEquivalent;
250 }
251
264 {
265 if (this->mostSigOp->template Is<DerivedT>()) {
266 auto generalizedMostSigOp = this->mostSigOp->Generalize();
267 const auto& mostSigOp = static_cast<const DerivedGeneralized&>(*generalizedMostSigOp);
268 mostSigOp.Flatten(out);
269 } else {
270 out.push_back(this->mostSigOp->Copy());
271 }
272
273 if (this->leastSigOp->template Is<DerivedT>()) {
274 auto generalizedLeastSigOp = this->leastSigOp->Generalize();
275 const auto& leastSigOp = static_cast<const DerivedGeneralized&>(*generalizedLeastSigOp);
276 leastSigOp.Flatten(out);
277 } else {
278 out.push_back(this->leastSigOp->Copy());
279 }
280 }
281
286 auto GetMostSigOp() const -> const MostSigOpT&
287 {
288 assert(mostSigOp != nullptr);
289 return *mostSigOp;
290 }
291
296 auto GetLeastSigOp() const -> const LeastSigOpT&
297 {
298 assert(leastSigOp != nullptr);
299 return *leastSigOp;
300 }
301
306 [[nodiscard]] auto HasMostSigOp() const -> bool
307 {
308 return mostSigOp != nullptr;
309 }
310
315 [[nodiscard]] auto HasLeastSigOp() const -> bool
316 {
317 return leastSigOp != nullptr;
318 }
319
324 template <typename T>
326 auto SetMostSigOp(const T& op) -> bool
327 {
329 this->mostSigOp = op.Copy();
330 return true;
331 }
332
334 this->mostSigOp = std::make_unique<MostSigOpT>(op);
335 return true;
336 }
337
338 if (auto castedOp = Oasis::RecursiveCast<MostSigOpT>(op); castedOp) {
339 mostSigOp = std::move(castedOp);
340 return true;
341 }
342
343 return false;
344 }
345
350 template <typename T>
352 auto SetLeastSigOp(const T& op) -> bool
353 {
355 this->leastSigOp = op.Copy();
356 return true;
357 }
358
360 this->leastSigOp = std::make_unique<LeastSigOpT>(op);
361 return true;
362 }
363
364 if (auto castedOp = Oasis::RecursiveCast<LeastSigOpT>(op); castedOp) {
365 leastSigOp = std::move(castedOp);
366 return true;
367 }
368
369 return false;
370 }
371
372 auto Substitute(const Expression& var, const Expression& val) -> std::unique_ptr<Expression> override
373 {
374 const std::unique_ptr<Expression> left = GetMostSigOp().Substitute(var, val);
375 const std::unique_ptr<Expression> right = GetLeastSigOp().Substitute(var, val);
376 DerivedGeneralized comb { *left, *right };
377 // auto ret = comb.Simplify();
378 // return ret;
379 return comb;
380 }
385 auto SwapOperands() const -> DerivedSpecialized
386 {
387 return DerivedT { *this->leastSigOp, *this->mostSigOp };
388 }
389
391
392 void Serialize(SerializationVisitor& visitor) const override
393 {
394 const auto generalized = Generalize();
395 const auto& derivedGeneralized = dynamic_cast<const DerivedGeneralized&>(*generalized);
396 visitor.Serialize(derivedGeneralized);
397 }
398
399private:
402};
403
404}
405
406#endif // OASIS_BOUNDEDBINARYEXPRESSION_HPP
A concept for an operand of a binary expression with bounds.
Definition BoundedBinaryExpression.hpp:25
auto Differentiate(const Expression &differentiationVariable) const -> std::unique_ptr< Expression > override
Tries to differentiate this function.
Definition BoundedBinaryExpression.hpp:74
auto SwapOperands() const -> DerivedSpecialized
Swaps the operands of this expression.
Definition BoundedBinaryExpression.hpp:385
auto GetMostSigOp() const -> const MostSigOpT &
Gets the most significant operand of this expression.
Definition BoundedBinaryExpression.hpp:286
auto Flatten(std::vector< std::unique_ptr< Expression > > &out) const -> void
Flattens this expression.
Definition BoundedBinaryExpression.hpp:263
auto Simplify(tf::Subflow &subflow) const -> std::unique_ptr< Expression > override
Definition BoundedBinaryExpression.hpp:167
BoundedBinaryExpression(const MostSigOpT &mostSigOp, const LeastSigOpT &leastSigOp)
Definition BoundedBinaryExpression.hpp:43
auto Substitute(const Expression &var, const Expression &val) -> std::unique_ptr< Expression > override
Definition BoundedBinaryExpression.hpp:372
auto Equals(const Expression &other) const -> bool final
Compares this expression to another expression for equality.
Definition BoundedBinaryExpression.hpp:78
auto SetMostSigOp(const T &op) -> bool
Sets the most significant operand of this expression.
Definition BoundedBinaryExpression.hpp:326
auto Generalize() const -> std::unique_ptr< Expression > final
Converts this expression to a more general expression.
Definition BoundedBinaryExpression.hpp:131
auto HasMostSigOp() const -> bool
Gets whether this expression has a most significant operand.
Definition BoundedBinaryExpression.hpp:306
void Serialize(SerializationVisitor &visitor) const override
Definition BoundedBinaryExpression.hpp:392
auto GetLeastSigOp() const -> const LeastSigOpT &
Gets the least significant operand of this expression.
Definition BoundedBinaryExpression.hpp:296
auto HasLeastSigOp() const -> bool
Gets whether this expression has a least significant operand.
Definition BoundedBinaryExpression.hpp:315
auto SetLeastSigOp(const T &op) -> bool
Sets the least significant operand of this expression.
Definition BoundedBinaryExpression.hpp:352
auto Generalize(tf::Subflow &subflow) const -> std::unique_ptr< Expression > final
Definition BoundedBinaryExpression.hpp:146
auto Copy(tf::Subflow &subflow) const -> std::unique_ptr< Expression > final
Definition BoundedBinaryExpression.hpp:54
auto Copy() const -> std::unique_ptr< Expression > final
Copies this expression.
Definition BoundedBinaryExpression.hpp:49
auto StructurallyEquivalent(const Expression &other) const -> bool final
Checks whether this expression is structurally equivalent to another expression.
Definition BoundedBinaryExpression.hpp:185
auto operator=(const BoundedBinaryExpression &other) -> BoundedBinaryExpression &=default
auto StructurallyEquivalent(const Expression &other, tf::Subflow &subflow) const -> bool final
Definition BoundedBinaryExpression.hpp:213
BoundedBinaryExpression(const BoundedBinaryExpression &other)
Definition BoundedBinaryExpression.hpp:32
A concept base class for both Unary and BoundedBinary expressions.
Definition BoundedExpression.hpp:23
An expression.
Definition Expression.hpp:63
virtual auto GetCategory() const -> uint32_t
Gets the category of this expression.
Definition Expression.cpp:219
virtual auto GetType() const -> ExpressionType
Gets the type of this expression.
Definition Expression.cpp:229
Checks if type T is same as any of the provided types in U.
Definition Concepts.hpp:53
T find_if(T... args)
T is_same_v
Definition Add.hpp:11
@ Associative
Definition Expression.hpp:50