Skip to content

Commit

Permalink
Support bit_not for Bool type (#2076)
Browse files Browse the repository at this point in the history
* support bit_not for Bool type

* add reno

* add test for bix_xor
  • Loading branch information
hhorii authored Mar 25, 2024
1 parent 7ea1c1a commit 1e01a47
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 25 deletions.
7 changes: 7 additions & 0 deletions releasenotes/notes/add_bit_not_for_bool-2aa62305e62a4f34.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
features:
- |
``bit_not`` is defined for ``Uint`` values. However, in other language,
depending hardware, ``Bool`` values can be applied as ``logical_not``
(``False`` is ``0`` and ``True`` is ``1``). This PR supports such implicit
operations for only "``bit_not``".
36 changes: 17 additions & 19 deletions src/framework/operations.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -222,30 +222,28 @@ class BoolValue : public ValueExpr {
class UnaryExpr : public CExpr {
public:
UnaryExpr(const UnaryOp op_, const std::shared_ptr<CExpr> operand_)
: CExpr(CExprType::Unary, operand_->type), op(op_), operand(operand_) {
if (op == UnaryOp::LogicNot && operand_->type->type != ValueType::Bool)
throw std::invalid_argument(
R"(LogicNot unary expression must has Bool expression as its operand.)");

if (op == UnaryOp::BitNot && operand_->type->type != ValueType::Uint)
throw std::invalid_argument(
R"(BitNot unary expression must has Uint expression as its operand.)");
}
: CExpr(CExprType::Unary, operand_->type), op(op_), operand(operand_) {}

virtual bool eval_bool(const std::string &memory) {
if (op == UnaryOp::BitNot)
throw std::invalid_argument(
R"(eval_bool is called for BitNot unary expression.)");
else // LogicNot
return !operand->eval_bool(memory);
if (op == UnaryOp::LogicNot || op == UnaryOp::BitNot) {
if (operand->type->type == ValueType::Bool) {
return !operand->eval_bool(memory);
} else if (operand->type->type == ValueType::Uint) {
return truncate(~operand->eval_uint(memory), type->width) != 0Ul;
}
}
throw std::invalid_argument(R"(should not reach here.)");
}

virtual uint_t eval_uint(const std::string &memory) {
if (op == UnaryOp::BitNot)
return truncate(~operand->eval_uint(memory), type->width);
else // LogicNot
throw std::invalid_argument(
R"(eval_uint is called for LogicNot unary expression.)");
if (op == UnaryOp::LogicNot || op == UnaryOp::BitNot) {
if (operand->type->type == ValueType::Bool) {
return operand->eval_bool(memory) ? 1UL : 0UL;
} else if (operand->type->type == ValueType::Uint) {
return truncate(~operand->eval_uint(memory), type->width);
}
}
throw std::invalid_argument(R"(should not reach here.)");
}

public:
Expand Down
104 changes: 98 additions & 6 deletions test/terra/backends/aer_simulator/test_control_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -1162,25 +1162,117 @@ def test_bit_or_operation(self, method):
@data("statevector", "density_matrix", "matrix_product_state", "stabilizer")
def test_bit_xor_operation(self, method):
"""test bit-or operation"""
qr = QuantumRegister(7)
cr = ClassicalRegister(7)
qr = QuantumRegister(8)
cr = ClassicalRegister(8)
qc = QuantumCircuit(qr, cr)
qc.x(0)
qc.x(2)
qc.measure(range(4), range(4)) # 0101
qc.barrier()
b01 = expr.bit_xor(cr[0], cr[1]) # 1 & 0 -> 1
b01 = expr.bit_xor(cr[0], cr[1]) # (bool) 1 & (bool) 0 -> (bool) 1
with qc.if_test(b01):
qc.x(4) # q4 -> 1

b02 = expr.bit_xor(cr[0], cr[2]) # 1 & 1 -> 0
b02 = expr.bit_xor(cr[0], cr[2]) # (bool) 1 & (bool) 1 -> (bool) 0
with qc.if_test(b02):
qc.x(5) # q5 -> 0

b13 = expr.bit_xor(cr[1], cr[3]) # 0 & 0 -> 0
with qc.if_test(b13):
b03 = expr.bit_xor(cr[1], cr[3]) # (bool) 0 & (bool) 0 -> (bool) 0
with qc.if_test(b03):
qc.x(6) # q6 -> 0

b04 = expr.bit_xor(
expr.Value(True, types.Bool()), expr.Value(False, types.Bool())
) # (bool) 1 & (bool) 0 -> (bool) 1
with qc.if_test(b04):
qc.x(7) # q7 -> 1

qc.measure(range(8), range(8)) # 10010101

backend = self.backend(method=method)
counts = backend.run(qc).result().get_counts()
self.assertEqual(len(counts), 1)
self.assertIn("10010101", counts)

qr = QuantumRegister(7)
cr = ClassicalRegister(7)
qc = QuantumCircuit(qr, cr)
qc.x(0)
qc.x(2)
qc.measure(range(4), range(4)) # 0101
qc.barrier()
try:
b04 = expr.bit_xor(
expr.Var(cr, types.Uint(cr.size)), expr.Var(cr, types.Uint(cr.size))
) # (bool) 1 ^ (uint) 0101 -> error
self.fail("do not reach here")
except Exception:
pass

qr = QuantumRegister(7)
cr = ClassicalRegister(7)
cr0 = ClassicalRegister(7)
qc = QuantumCircuit(qr, cr, cr0)
qc.x(0)
qc.x(1)
qc.x(2)
qc.x(3)
qc.measure(range(4), range(4)) # 1111
qc.barrier()
b05 = expr.bit_xor(
expr.Var(cr, types.Uint(cr.size)), expr.Var(cr, types.Uint(cr.size))
) # (uint) 1111 ^ (uint) 1111 -> (uint) 0000
with qc.if_test(expr.equal(b05, 0b0000000)):
qc.x(4) # q4 -> 1
b06 = expr.bit_xor(
expr.Var(cr0, types.Uint(cr0.size)), expr.Var(cr, types.Uint(cr.size))
) # (uint) 0000 ^ (uint) 0101 -> (uint) 1111
with qc.if_test(expr.equal(b06, 0b0001111)):
qc.x(5) # q5 -> 1

qc.measure(range(7), range(7)) # 111111

backend = self.backend(method=method)
counts = backend.run(qc).result().get_counts()
self.assertEqual(len(counts), 1)
self.assertIn("0000000 0111111", counts)

@data("statevector", "density_matrix", "matrix_product_state", "stabilizer")
def test_bit_not_operation(self, method):
"""test bit-not operation"""
qr = QuantumRegister(7)
cr = ClassicalRegister(7)
qc = QuantumCircuit(qr, cr)
qc.x(0)
qc.x(2)
qc.measure(range(4), range(4)) # 0101
qc.barrier()
b01 = expr.bit_not(cr[0]) # !1 -> 0
with qc.if_test(b01):
qc.x(4) # q4 -> 0

b02 = expr.bit_not(cr[1]) # !0 -> 1
with qc.if_test(b02):
qc.x(5) # q5 -> 1

qc.measure(range(7), range(7)) # 0100101

backend = self.backend(method=method)
counts = backend.run(qc).result().get_counts()
self.assertEqual(len(counts), 1)
self.assertIn("0100101", counts)

qr = QuantumRegister(7)
cr = ClassicalRegister(7)
qc = QuantumCircuit(qr, cr)
qc.x(0)
qc.x(2)
qc.measure(range(4), range(4)) # 0101
qc.barrier()
b01 = expr.bit_not(expr.Var(cr, types.Uint(cr.size))) # 0b0000101 -> 0b1111010
with qc.if_test(expr.equal(b01, 0b1111010)):
qc.x(4) # q4 -> 1

qc.measure(range(7), range(7)) # 0010101

backend = self.backend(method=method)
Expand Down

0 comments on commit 1e01a47

Please sign in to comment.