Special Topics

This page provides detailed information on specialized functionalities in MIMIQ.

Repeated Targets

By default, MIMIQ does not allow repeated targets in a single instruction. This means that if an operation acts on several targets of the same register, those targets are normally expected to be distinct.

Qubit targets are a special case: they can never be repeated.

For some operations, repeated classical-bit or Z-register targets are meaningful and therefore explicitly allowed. This is useful when the same register is both read and written by the operation.

Repeated classical-bit targets

Some classical and control-flow operations allow repeated classical-bit targets. This includes IfStatement, WhileStatement, And, Or, and Xor.

For IfStatement and WhileStatement, this means that the same classical register can appear both in the body of the operation and in the condition bits.

c = Circuit()

# Toggle c[1] if c[1] == 1
push!(c, IfStatement(Not(), bs"1"), 1, 1)
1-bit circuit with 1 instruction:
└── IF(c==1) ~ @ c[1], condition[1]
c = Circuit()

# Repeatedly apply Not() while c[1] == 1
push!(c, WhileStatement(Not(), bs"1"), 1, 1)
1-bit circuit with 1 instruction:
└── WHILE(c==1) ~ @ c[1], condition[1]

For classical Boolean operations such as And, Or, and Xor, repeated bit targets allow in-place updates.

c = Circuit()
push!(c, And(), 1, 1, 2)
2-bit circuit with 1 instruction:
└── c[1] = c[1] & c[2]

This means that the result is written to c[1] while also reading c[1] as one of the inputs.

Repeated Z-register targets

Some Z-register operations also allow repeated targets. This includes Add, Multiply, Pow, and also the wrapper operations IfStatement and WhileStatement.

This is useful for in-place updates of Z-register variables.

c = Circuit()
push!(c, Add(3), 1, 1, 2)
2-vars circuit with 1 instruction:
└── z[1] = z[1] + z[2]
push!(c, Multiply(3), 1, 1, 2)
2-vars circuit with 2 instructions:
├── z[1] = z[1] + z[2]
└── z[1] = z[1] * z[2]

In the first case we compute z[1] = z[1] + z[2], and in the second case z[1] = z[1] * z[2].

BitString

The BitString class represents the state of bits and can be used to represent classical registers with specified values for each bit (0 or 1). At its core, it is simply a vector of Bools. BitString allows direct bit manipulation, bitwise operations, and conversion to other data formats like integers. It’s designed for flexibility in binary manipulation tasks within quantum computations.

Using BitString in MIMIQ Operations

In MIMIQ, several operations use BitString as a direct input for conditional logic or specific quantum operations, such as IfStatement, WhileStatement, and Amplitude, see non-unitary operations and statistical operations pages. Here are some examples:

# Conditional Operation: IfStatement
if_statement = IfStatement(GateX(), BitString("01011"))

# Conditional Loop: WhileStatement
while_statement = WhileStatement(Not(), BitString("1"))

# Amplitude Operation
Amplitude(BitString("001"))
Amplitude(bs"001")

Constructors

BitStrings can be constructed in different ways.

  • From a String: You can use the BitString("binary_string") to initialize a BitString by parsing a string in binary format.

    # Initialize a BitString from a binary string representation
    BitString("1101")
    4-bits BitString with integer value 11:
      1101
    

    Alternatively, you can use the syntax bs"binary_string" to create the same BitString.

    # Initialize a BitString using the bs"..." literal syntax
    bs"01"
    2-bits BitString with integer value 2:
      01
    
  • From bit locations: You can use the BitString(numbits[; bit_indices]) syntax to initialize a BitString of numbits bits, setting specific bits indicated by bit_indices to 1.

    # Initializing with Specific Bits
    BitString(8, [2, 4, 6])
    8-bits BitString with integer value 42:
      01010100
    
  • From a function: You can use the BitString(f::Function, numbits) syntax to initialize a BitString with numbits, where each bit is set based on the result of the provided function f applied on each index.

    # Initialize an 8-bit BitString where bits are set based on even indices
    BitString(8) do i
            iseven(i)
          end
    8-bits BitString with integer value 170:
      01010101
    

Accessing and Modifying Bits

Each bit in a BitString can be accessed or modified individually in the same way as vectors, making it easy to retrieve or set specific bit values.

using MimiqCircuits # hide

# Accessing a Bit
bs=BitString(4, [1, 3])

println(bs[2])

# Modifying a Bit
bs[2] = true

bs

A useful function is nonzeros which returns the indices of the non-zero bits in a BitString.

  # Retrieve Non-Zero Indices
  nonzeros(bs)
3-element Vector{Int64}:
 1
 3
 5

Conversion and Manipulation Methods

The BitString class includes functionality for conversion to integer representations, indexing, and other methods for retrieving and manipulating bit values:

  • BitString to Integer: To convert a BitString into its integer representation, you can use the function bitstring_to_integer. By default it uses a big-endian order.

    bs = BitString("101010")
    
    # Convert BitString to Integer (big-endian by default)
    bitstring_to_integer(bs)
    21

    Alternatively, you can use the function bitstring_to_index, which converts a BitString to an index for purposes like vector indexing, checking bounds, and compatibility with 64-bit indexing constraints. It's essentially the same as bitstring_to_integer but shifted by 1.

    bs = BitString("101010")
    
    # Convert BitString to Index (Offset by 1 for Julia's 1-based indexing)
    bitstring_to_index(bs)
    22
  • BitString to String: To convert a BitString into a String of "0" and "1" characters, you can use the function to01.

    bs = BitString("101010")
    
    # Convert BitString to String of "0"s and "1"s (big-endian)
    println(to01(bs))
    
    # Convert BitString to String of "0"s and "1"s (little-endian)
    to01(bs, endianess=:little)
    "010101"

Bitwise Operators

BitString supports bitwise operations such as NOT, AND, OR, XOR, as well as bitwise shifts:

  • Bitwise NOT: ~

      bs = BitString("1011")
    
      # Bitwise NOT
      ~bs
    4-bits BitString with integer value 2:
      0100
    
  • Bitwise AND and OR: &, |

      bs1 = BitString("1100")
      bs2 = BitString("0110")
    
      # Bitwise AND
      bs1 & bs2
    4-bits BitString with integer value 2:
      0100
    
      # Bitwise OR
      bs1 | bs2
    4-bits BitString with integer value 7:
      1110
    
  • Bitwise XOR:

      # Bitwise XOR
      bs1 ⊻ bs2
    4-bits BitString with integer value 5:
      1010
    
  • Left Shift: <<, and Right Shift: >>

      # Left Shift
      bs << 1
    4-bits BitString with integer value 14:
      0111
    
      # Right Shift
      bs >> 1
    4-bits BitString with integer value 11:
      1101
    

Concatenation and Repetition

BitString supports concatenation and repetition, allowing you to combine or extend bitstrings efficiently:

  • Concatenation: Use vcat to combines two BitString objects by appending the bits of rhs to lhs.

  • Repetition: Use repeat to repeats the BitString a specified number of times, creating a new BitString with the pattern repeated.

Examples:

# Define two BitString objects
bs1 = BitString("1010")
bs2 = BitString("0101")

# Concatenate bs1 and bs2
vcat(bs1, bs2)
8-bits BitString with integer value 165:
  10100101
# Repeat bs1 two times
repeat(bs1, 2)
8-bits BitString with integer value 85:
  10101010