Skip to content

Commit

Permalink
Fix multiplication and division of complex numbers
Browse files Browse the repository at this point in the history
Multiplication and division of complex numbers are not just pointwise
applications of those operations.

Fixes: #8375
  • Loading branch information
tautschnig committed Jul 12, 2024
1 parent 629dbcd commit e449479
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 44 deletions.
15 changes: 15 additions & 0 deletions regression/cbmc/complex2/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#include <complex.h>

int main()
{
char choice;
float re = choice ? 1.3f : 2.1f; // a non-constant well-behaved float
_Complex float z1 = I + re;
_Complex float z2 = z1 * z1;
_Complex float expected = 2 * I * re + re * re - 1; // (a+i)^2 = 2ai + a^2 - 1
_Complex float actual =
re * re + I; // (a1 + b1*i)*(a2 + b2*i) = (a1*a2 + b1*b2*i)
__CPROVER_assert(z2 == expected, "right");
__CPROVER_assert(z2 == actual, "wrong");
return 0;
}
13 changes: 13 additions & 0 deletions regression/cbmc/complex2/test.desc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
CORE no-new-smt gcc-only
main.c

^\[main.assertion.1\] line 12 right: SUCCESS$
^\[main.assertion.2\] line 13 wrong: FAILURE$
^VERIFICATION FAILED$
^EXIT=10$
^SIGNAL=0$
--
^warning: ignoring
--
Visual Studio does not directly support `complex` or `_Complex`, or using
standard arithmetic operators over complex numbers.
56 changes: 50 additions & 6 deletions src/goto-programs/remove_complex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,13 +127,10 @@ static void remove_complex(exprt &expr)

if(expr.type().id()==ID_complex)
{
if(expr.id()==ID_plus || expr.id()==ID_minus ||
expr.id()==ID_mult || expr.id()==ID_div)
if(expr.id() == ID_plus || expr.id() == ID_minus)
{
// FIXME plus and mult are defined as n-ary operations
// rather than binary. This code assumes that they
// can only have exactly 2 operands, and it is not clear
// that it is safe to do so in this context
// plus and mult are n-ary expressions, but front-ends currently ensure
// that we only see them as binary ones
PRECONDITION(expr.operands().size() == 2);
// do component-wise:
// x+y -> complex(x.r+y.r,x.i+y.i)
Expand All @@ -153,6 +150,53 @@ static void remove_complex(exprt &expr)

expr=struct_expr;
}
else if(expr.id() == ID_mult)
{
// plus and mult are n-ary expressions, but front-ends currently ensure
// that we only see them as binary ones

Check warning on line 156 in src/goto-programs/remove_complex.cpp

View check run for this annotation

Codecov / codecov/patch

src/goto-programs/remove_complex.cpp#L156

Added line #L156 was not covered by tests
PRECONDITION(expr.operands().size() == 2);
exprt lhs_real = complex_member(to_binary_expr(expr).op0(), ID_real);
exprt lhs_imag = complex_member(to_binary_expr(expr).op0(), ID_imag);
exprt rhs_real = complex_member(to_binary_expr(expr).op1(), ID_real);
exprt rhs_imag = complex_member(to_binary_expr(expr).op1(), ID_imag);

struct_exprt struct_expr{
{minus_exprt{
mult_exprt{lhs_real, rhs_real}, mult_exprt{lhs_imag, rhs_imag}},
plus_exprt{
mult_exprt{lhs_imag, rhs_real}, mult_exprt{lhs_real, rhs_imag}}},
expr.type()};

struct_expr.op0().add_source_location() = expr.source_location();
struct_expr.op1().add_source_location() = expr.source_location();

expr = struct_expr;
}
else if(expr.id() == ID_div)
{
exprt lhs_real = complex_member(to_binary_expr(expr).op0(), ID_real);
exprt lhs_imag = complex_member(to_binary_expr(expr).op0(), ID_imag);
exprt rhs_real = complex_member(to_binary_expr(expr).op1(), ID_real);
exprt rhs_imag = complex_member(to_binary_expr(expr).op1(), ID_imag);

Check warning on line 180 in src/goto-programs/remove_complex.cpp

View check run for this annotation

Codecov / codecov/patch

src/goto-programs/remove_complex.cpp#L177-L180

Added lines #L177 - L180 were not covered by tests

plus_exprt numerator_real{
mult_exprt{lhs_real, rhs_real}, mult_exprt{lhs_imag, rhs_imag}};
minus_exprt numerator_imag{
mult_exprt{lhs_imag, rhs_real}, mult_exprt{lhs_real, rhs_imag}};

Check warning on line 185 in src/goto-programs/remove_complex.cpp

View check run for this annotation

Codecov / codecov/patch

src/goto-programs/remove_complex.cpp#L182-L185

Added lines #L182 - L185 were not covered by tests

plus_exprt denominator{
mult_exprt{rhs_real, rhs_real}, mult_exprt{rhs_imag, rhs_imag}};

Check warning on line 188 in src/goto-programs/remove_complex.cpp

View check run for this annotation

Codecov / codecov/patch

src/goto-programs/remove_complex.cpp#L187-L188

Added lines #L187 - L188 were not covered by tests

struct_exprt struct_expr{
{div_exprt{numerator_real, denominator},
div_exprt{numerator_imag, denominator}},
expr.type()};

Check warning on line 193 in src/goto-programs/remove_complex.cpp

View check run for this annotation

Codecov / codecov/patch

src/goto-programs/remove_complex.cpp#L190-L193

Added lines #L190 - L193 were not covered by tests

struct_expr.op0().add_source_location() = expr.source_location();
struct_expr.op1().add_source_location() = expr.source_location();

Check warning on line 196 in src/goto-programs/remove_complex.cpp

View check run for this annotation

Codecov / codecov/patch

src/goto-programs/remove_complex.cpp#L195-L196

Added lines #L195 - L196 were not covered by tests

expr = struct_expr;

Check warning on line 198 in src/goto-programs/remove_complex.cpp

View check run for this annotation

Codecov / codecov/patch

src/goto-programs/remove_complex.cpp#L198

Added line #L198 was not covered by tests
}
else if(expr.id()==ID_unary_minus)
{
auto const &unary_minus_expr = to_unary_minus_expr(expr);
Expand Down
96 changes: 58 additions & 38 deletions src/solvers/flattening/boolbv_floatbv_op.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,14 @@ Author: Daniel Kroening, [email protected]
\*******************************************************************/

#include "boolbv.h"

#include <algorithm>

#include <util/bitvector_types.h>
#include <util/c_types.h>
#include <util/floatbv_expr.h>

#include <solvers/floatbv/float_utils.h>

#include "boolbv.h"

bvt boolbvt::convert_floatbv_typecast(const floatbv_typecast_exprt &expr)
{
const exprt &op0=expr.op(); // number to convert
Expand Down Expand Up @@ -131,44 +129,66 @@ bvt boolbvt::convert_floatbv_op(const ieee_float_op_exprt &expr)
sub_width > 0 && width % sub_width == 0,
"width of a complex subtype must be positive and evenly divide the "
"width of the complex expression");
DATA_INVARIANT(

Check warning on line 132 in src/solvers/flattening/boolbv_floatbv_op.cpp

View check run for this annotation

Codecov / codecov/patch

src/solvers/flattening/boolbv_floatbv_op.cpp#L132

Added line #L132 was not covered by tests
sub_width * 2 == width, "a complex type consists of exactly two parts");

std::size_t size=width/sub_width;
bvt result_bv;
result_bv.resize(width);
bvt lhs_real{lhs_as_bv.begin(), lhs_as_bv.begin() + sub_width};
bvt rhs_real{rhs_as_bv.begin(), rhs_as_bv.begin() + sub_width};

Check warning on line 136 in src/solvers/flattening/boolbv_floatbv_op.cpp

View check run for this annotation

Codecov / codecov/patch

src/solvers/flattening/boolbv_floatbv_op.cpp#L135-L136

Added lines #L135 - L136 were not covered by tests

for(std::size_t i=0; i<size; i++)
bvt lhs_imag{lhs_as_bv.begin() + sub_width, lhs_as_bv.end()};
bvt rhs_imag{rhs_as_bv.begin() + sub_width, rhs_as_bv.end()};

Check warning on line 139 in src/solvers/flattening/boolbv_floatbv_op.cpp

View check run for this annotation

Codecov / codecov/patch

src/solvers/flattening/boolbv_floatbv_op.cpp#L138-L139

Added lines #L138 - L139 were not covered by tests

bvt result_real, result_imag;

Check warning on line 141 in src/solvers/flattening/boolbv_floatbv_op.cpp

View check run for this annotation

Codecov / codecov/patch

src/solvers/flattening/boolbv_floatbv_op.cpp#L141

Added line #L141 was not covered by tests

if(expr.id() == ID_floatbv_plus || expr.id() == ID_floatbv_minus)
{
result_real = float_utils.add_sub(
lhs_real, rhs_real, expr.id() == ID_floatbv_minus);
result_imag = float_utils.add_sub(
lhs_imag, rhs_imag, expr.id() == ID_floatbv_minus);

Check warning on line 148 in src/solvers/flattening/boolbv_floatbv_op.cpp

View check run for this annotation

Codecov / codecov/patch

src/solvers/flattening/boolbv_floatbv_op.cpp#L145-L148

Added lines #L145 - L148 were not covered by tests
}
else if(expr.id() == ID_floatbv_mult)

Check warning on line 150 in src/solvers/flattening/boolbv_floatbv_op.cpp

View check run for this annotation

Codecov / codecov/patch

src/solvers/flattening/boolbv_floatbv_op.cpp#L150

Added line #L150 was not covered by tests
{
// Could be optimised to just three multiplications with more additions
// instead, but then we'd have to worry about the impact of possible
// overflows. So we use the naive approach for now:
result_real = float_utils.add_sub(
float_utils.mul(lhs_real, rhs_real),
float_utils.mul(lhs_imag, rhs_imag),
true);
result_imag = float_utils.add_sub(
float_utils.mul(lhs_real, rhs_imag),
float_utils.mul(lhs_imag, rhs_real),
false);

Check warning on line 162 in src/solvers/flattening/boolbv_floatbv_op.cpp

View check run for this annotation

Codecov / codecov/patch

src/solvers/flattening/boolbv_floatbv_op.cpp#L152-L162

Added lines #L152 - L162 were not covered by tests
}
else if(expr.id() == ID_floatbv_div)

Check warning on line 164 in src/solvers/flattening/boolbv_floatbv_op.cpp

View check run for this annotation

Codecov / codecov/patch

src/solvers/flattening/boolbv_floatbv_op.cpp#L164

Added line #L164 was not covered by tests
{
bvt lhs_sub_bv, rhs_sub_bv, sub_result_bv;

lhs_sub_bv.assign(
lhs_as_bv.begin() + i * sub_width,
lhs_as_bv.begin() + (i + 1) * sub_width);
rhs_sub_bv.assign(
rhs_as_bv.begin() + i * sub_width,
rhs_as_bv.begin() + (i + 1) * sub_width);

if(expr.id()==ID_floatbv_plus)
sub_result_bv = float_utils.add_sub(lhs_sub_bv, rhs_sub_bv, false);
else if(expr.id()==ID_floatbv_minus)
sub_result_bv = float_utils.add_sub(lhs_sub_bv, rhs_sub_bv, true);
else if(expr.id()==ID_floatbv_mult)
sub_result_bv = float_utils.mul(lhs_sub_bv, rhs_sub_bv);
else if(expr.id()==ID_floatbv_div)
sub_result_bv = float_utils.div(lhs_sub_bv, rhs_sub_bv);
else
UNREACHABLE;

INVARIANT(
sub_result_bv.size() == sub_width,
"we constructed a new complex of the right size");
INVARIANT(
i * sub_width + sub_width - 1 < result_bv.size(),
"the sub-bitvector fits into the result bitvector");
std::copy(
sub_result_bv.begin(),
sub_result_bv.end(),
result_bv.begin() + i * sub_width);
bvt numerator_real = float_utils.add_sub(
float_utils.mul(lhs_real, rhs_real),
float_utils.mul(lhs_imag, rhs_imag),
false);
bvt numerator_imag = float_utils.add_sub(
float_utils.mul(lhs_imag, rhs_real),
float_utils.mul(lhs_real, rhs_imag),
true);

Check warning on line 173 in src/solvers/flattening/boolbv_floatbv_op.cpp

View check run for this annotation

Codecov / codecov/patch

src/solvers/flattening/boolbv_floatbv_op.cpp#L166-L173

Added lines #L166 - L173 were not covered by tests

bvt denominator = float_utils.add_sub(
float_utils.mul(rhs_real, rhs_real),
float_utils.mul(rhs_imag, rhs_imag),
false);

Check warning on line 178 in src/solvers/flattening/boolbv_floatbv_op.cpp

View check run for this annotation

Codecov / codecov/patch

src/solvers/flattening/boolbv_floatbv_op.cpp#L175-L178

Added lines #L175 - L178 were not covered by tests

result_real = float_utils.div(numerator_real, denominator);
result_imag = float_utils.div(numerator_imag, denominator);

Check warning on line 181 in src/solvers/flattening/boolbv_floatbv_op.cpp

View check run for this annotation

Codecov / codecov/patch

src/solvers/flattening/boolbv_floatbv_op.cpp#L180-L181

Added lines #L180 - L181 were not covered by tests
}
else
UNREACHABLE;

Check warning on line 184 in src/solvers/flattening/boolbv_floatbv_op.cpp

View check run for this annotation

Codecov / codecov/patch

src/solvers/flattening/boolbv_floatbv_op.cpp#L183-L184

Added lines #L183 - L184 were not covered by tests

bvt result_bv = std::move(result_real);
result_bv.reserve(width);
result_bv.insert(
result_bv.end(),
std::make_move_iterator(result_imag.begin()),
std::make_move_iterator(result_imag.end()));

Check warning on line 191 in src/solvers/flattening/boolbv_floatbv_op.cpp

View check run for this annotation

Codecov / codecov/patch

src/solvers/flattening/boolbv_floatbv_op.cpp#L186-L191

Added lines #L186 - L191 were not covered by tests

return result_bv;
}
Expand Down

0 comments on commit e449479

Please sign in to comment.