GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/arith/arith_poly_norm.cpp Lines: 121 125 96.8 %
Date: 2021-11-07 Branches: 184 403 45.7 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Andrew Reynolds
4
 *
5
 * This file is part of the cvc5 project.
6
 *
7
 * Copyright (c) 2009-2021 by the authors listed in the file AUTHORS
8
 * in the top-level source directory and their institutional affiliations.
9
 * All rights reserved.  See the file COPYING in the top-level source
10
 * directory for licensing information.
11
 * ****************************************************************************
12
 *
13
 * Arithmetic utility for polynomial normalization
14
 */
15
16
#include "theory/arith/arith_poly_norm.h"
17
18
using namespace cvc5::kind;
19
20
namespace cvc5 {
21
namespace theory {
22
namespace arith {
23
24
298
void PolyNorm::addMonomial(TNode x, const Rational& c, bool isNeg)
25
{
26
298
  Assert(c.sgn() != 0);
27
298
  std::unordered_map<Node, Rational>::iterator it = d_polyNorm.find(x);
28
298
  if (it == d_polyNorm.end())
29
  {
30
272
    d_polyNorm[x] = isNeg ? -c : c;
31
272
    return;
32
  }
33
52
  Rational res(it->second + (isNeg ? -c : c));
34
26
  if (res.sgn() == 0)
35
  {
36
    // cancels
37
8
    d_polyNorm.erase(it);
38
  }
39
  else
40
  {
41
18
    d_polyNorm[x] = res;
42
  }
43
}
44
45
28
void PolyNorm::multiplyMonomial(TNode x, const Rational& c)
46
{
47
28
  Assert(c.sgn() != 0);
48
28
  if (x.isNull())
49
  {
50
    // multiply by constant
51
14
    for (std::pair<const Node, Rational>& m : d_polyNorm)
52
    {
53
      // c1*x*c2 = (c1*c2)*x
54
8
      m.second *= c;
55
    }
56
  }
57
  else
58
  {
59
44
    std::unordered_map<Node, Rational> ptmp = d_polyNorm;
60
22
    d_polyNorm.clear();
61
44
    for (const std::pair<const Node, Rational>& m : ptmp)
62
    {
63
      // c1*x1*c2*x2 = (c1*c2)*(x1*x2)
64
44
      Node newM = multMonoVar(m.first, x);
65
22
      d_polyNorm[newM] = m.second * c;
66
    }
67
  }
68
28
}
69
70
156
void PolyNorm::add(const PolyNorm& p)
71
{
72
314
  for (const std::pair<const Node, Rational>& m : p.d_polyNorm)
73
  {
74
158
    addMonomial(m.first, m.second);
75
  }
76
156
}
77
78
10
void PolyNorm::subtract(const PolyNorm& p)
79
{
80
30
  for (const std::pair<const Node, Rational>& m : p.d_polyNorm)
81
  {
82
20
    addMonomial(m.first, m.second, true);
83
  }
84
10
}
85
86
26
void PolyNorm::multiply(const PolyNorm& p)
87
{
88
26
  if (p.d_polyNorm.size() == 1)
89
  {
90
40
    for (const std::pair<const Node, Rational>& m : p.d_polyNorm)
91
    {
92
20
      multiplyMonomial(m.first, m.second);
93
    }
94
  }
95
  else
96
  {
97
    // If multiplying by sum, must distribute; if multiplying by zero, clear.
98
    // First, remember the current state and clear.
99
12
    std::unordered_map<Node, Rational> ptmp = d_polyNorm;
100
6
    d_polyNorm.clear();
101
14
    for (const std::pair<const Node, Rational>& m : p.d_polyNorm)
102
    {
103
16
      PolyNorm pbase;
104
8
      pbase.d_polyNorm = ptmp;
105
8
      pbase.multiplyMonomial(m.first, m.second);
106
      // add this to current
107
8
      add(pbase);
108
    }
109
  }
110
26
}
111
112
void PolyNorm::clear() { d_polyNorm.clear(); }
113
114
138
bool PolyNorm::empty() const { return d_polyNorm.empty(); }
115
116
28
bool PolyNorm::isEqual(const PolyNorm& p) const
117
{
118
28
  if (d_polyNorm.size() != p.d_polyNorm.size())
119
  {
120
2
    return false;
121
  }
122
26
  std::unordered_map<Node, Rational>::const_iterator it;
123
68
  for (const std::pair<const Node, Rational>& m : d_polyNorm)
124
  {
125
44
    Assert(m.second.sgn() != 0);
126
44
    it = p.d_polyNorm.find(m.first);
127
44
    if (it == p.d_polyNorm.end() || m.second != it->second)
128
    {
129
2
      return false;
130
    }
131
  }
132
24
  return true;
133
}
134
135
22
Node PolyNorm::multMonoVar(TNode m1, TNode m2)
136
{
137
44
  std::vector<TNode> vars = getMonoVars(m1);
138
44
  std::vector<TNode> vars2 = getMonoVars(m2);
139
22
  vars.insert(vars.end(), vars2.begin(), vars2.end());
140
22
  if (vars.empty())
141
  {
142
    // constants
143
    return Node::null();
144
  }
145
22
  else if (vars.size() == 1)
146
  {
147
10
    return vars[0];
148
  }
149
  // use default sorting
150
12
  std::sort(vars.begin(), vars.end());
151
12
  return NodeManager::currentNM()->mkNode(NONLINEAR_MULT, vars);
152
}
153
154
44
std::vector<TNode> PolyNorm::getMonoVars(TNode m)
155
{
156
44
  std::vector<TNode> vars;
157
  // m is null if this is the empty variable (for constant monomials)
158
44
  if (!m.isNull())
159
  {
160
34
    Kind k = m.getKind();
161
34
    Assert(k != CONST_RATIONAL);
162
34
    if (k == MULT || k == NONLINEAR_MULT)
163
    {
164
      vars.insert(vars.end(), m.begin(), m.end());
165
    }
166
    else
167
    {
168
34
      vars.push_back(m);
169
    }
170
  }
171
44
  return vars;
172
}
173
174
56
PolyNorm PolyNorm::mkPolyNorm(TNode n)
175
{
176
56
  Assert(n.getType().isReal());
177
112
  Rational one(1);
178
112
  Node null;
179
112
  std::unordered_map<TNode, PolyNorm> visited;
180
56
  std::unordered_map<TNode, PolyNorm>::iterator it;
181
112
  std::vector<TNode> visit;
182
112
  TNode cur;
183
56
  visit.push_back(n);
184
292
  do
185
  {
186
348
    cur = visit.back();
187
348
    it = visited.find(cur);
188
348
    Kind k = cur.getKind();
189
348
    if (it == visited.end())
190
    {
191
210
      if (k == CONST_RATIONAL)
192
      {
193
56
        Rational r = cur.getConst<Rational>();
194
28
        if (r.sgn() == 0)
195
        {
196
          // zero is not an entry
197
10
          visited[cur] = PolyNorm();
198
        }
199
        else
200
        {
201
18
          visited[cur].addMonomial(null, r);
202
        }
203
      }
204
182
      else if (k == PLUS || k == MINUS || k == UMINUS || k == MULT
205
102
               || k == NONLINEAR_MULT)
206
      {
207
80
        visited[cur] = PolyNorm();
208
264
        for (const Node& cn : cur)
209
        {
210
184
          visit.push_back(cn);
211
80
        }
212
      }
213
      else
214
      {
215
        // it is a leaf
216
102
        visited[cur].addMonomial(cur, one);
217
102
        visit.pop_back();
218
      }
219
210
      continue;
220
    }
221
138
    visit.pop_back();
222
138
    if (it->second.empty())
223
    {
224
90
      PolyNorm& ret = visited[cur];
225
90
      switch (k)
226
      {
227
80
        case PLUS:
228
        case MINUS:
229
        case UMINUS:
230
        case MULT:
231
        case NONLINEAR_MULT:
232
264
          for (size_t i = 0, nchild = cur.getNumChildren(); i < nchild; i++)
233
          {
234
184
            it = visited.find(cur[i]);
235
184
            Assert(it != visited.end());
236
184
            if ((k == MINUS && i == 1) || k == UMINUS)
237
            {
238
10
              ret.subtract(it->second);
239
            }
240
174
            else if (i > 0 && (k == MULT || k == NONLINEAR_MULT))
241
            {
242
26
              ret.multiply(it->second);
243
            }
244
            else
245
            {
246
148
              ret.add(it->second);
247
            }
248
80
          }
249
80
          break;
250
10
        case CONST_RATIONAL: break;
251
        default: Unhandled() << "Unhandled polynomial operation " << cur; break;
252
      }
253
    }
254
348
  } while (!visit.empty());
255
56
  Assert(visited.find(n) != visited.end());
256
112
  return visited[n];
257
}
258
259
28
bool PolyNorm::isArithPolyNorm(TNode a, TNode b)
260
{
261
56
  PolyNorm pa = PolyNorm::mkPolyNorm(a);
262
56
  PolyNorm pb = PolyNorm::mkPolyNorm(b);
263
56
  return pa.isEqual(pb);
264
}
265
266
}  // namespace arith
267
}  // namespace theory
268
31137
}  // namespace cvc5