view interps/qbf/qubit.py @ 12518:2d8fe55c6e65 draft default tip

<int-e> learn The password of the month is release incident pilot.
author HackEso <hackeso@esolangs.org>
date Sun, 03 Nov 2024 00:31:02 +0000
parents 859f9b4339e6
children
line wrap: on
line source

# A qubit library
# Author: Nikita Ayzikovsky (lament)
# You may use this freely, but it's neither GPL nor public domain (yet)

# Note that everything starts out initialized to 1. This needs to be
# changed if this is to be used for something other than Quantum Brainfuck.
# Other than that, the library is completely generic. Just add your own
# gates and go ahead.

# The things that should be actually used from outside are
# clases Gate and Register.

import random # of course!

def dotproduct(a,b):
    """Because I'm too lazy to look for a lin. algebra module"""
    return sum([x*y for (x,y) in zip(a,b)])

def mvmult(matrix, vector):
    """Because I'm too lazy to look for a lin. algebra module"""
    #no error checking for now
    result = []
    for row in matrix:
        result.append(dotproduct(row, vector))
    return result

def bit(num, bitnum):
    return int((num&(1<<bitnum)) != 0)

def n2bits(n, length):
    return [bit(n, x) for x in range(length)]

def bits2n(bitlist):
    result = 0
    for x in range(len(bitlist)):
        result += bitlist[x] * (1<<x)
    return result

class Gate:
    def __init__(self, num_bits, matrix):
        self.num_bits = num_bits
        self.N = 1<<num_bits
        self.matrix = matrix
    def apply(self, register, bitlist):
        """Applies this gate to bits from bitlist (size of bitlist should
        be N) in the register object given"""
        # let's do this the least efficient way possible
        # apply the gate to bits in bitlist for each individual combination
        # of other bits
        # We have register.length total bits, so register.length-N
        # bit combinations

        new_contents = register.contents[:]

        num_bits = register.length
        allbits = range(num_bits)
        otherbits = [x for x in allbits if x not in bitlist]
        # Iterate over each combination of other bits:
        for i in range(2**len(otherbits)):
            other_bit_values = n2bits(i, len(otherbits))
            positions = []
            for j in range(self.N):
                current_position = [0] * num_bits
                for index, value in zip(otherbits, other_bit_values):
                    current_position[index] = value
                for index, value in zip(bitlist,n2bits(j, self.N)):
                    current_position[index] = value
                positions.append(bits2n(current_position))
            values = [register.contents[x] for x in positions]
            values = mvmult(self.matrix, values)
            for x, index in zip(positions, range(self.N)):
                new_contents[x] = values[index]
        register.contents = new_contents

class Register:
    def __init__(self, length):
        """Initialize to all 1s as per Quantum Brainfuck specs"""
        self.length = length
        self.contents = [0] * (2**length)
        self.contents[-1] = 1
    def add_bit(self):
        """Adds a new qubit, containing 1 and not entangled with anything"""
        new_contents = []
        new_contents = [0] * len(self.contents) + self.contents
        self.contents = new_contents
        self.length += 1
    def observe(self, bit_index):
        """Observes the value of the qubit at the given index,
        changing that bit to |0> or |1> and updating all probabilities
        accordingly. Returns 0 or 1"""

        # first, find out the probability that the qubit is set.
        prob = 0
        for i in range(2 ** self.length):
            prob +=  abs(bit(i, bit_index) * self.contents[i]) ** 2
        # prob is now set to the probability of bit set to 1
        # now "Observe"
        if random.random() <= prob:
            bit_value = 1
        else:
            bit_value = 0
        # now that we know the "observed" value, adjust all other
        # probabilities to account for it.
        if prob == 0 or prob == 1:
            # don't need adjustment
            return bit_value

        adjustment = (1 / prob) ** 0.5
        for i in range(2 ** self.length):
            if bit(i, bit_index) == bit_value:
                self.contents[i] = self.contents[i] * adjustment
            else:
                self.contents[i] = 0
        return bit_value

    def set(self, bit_index, bit_value):
        """Sets the indexed bit to 'value', which should be 1 or 0"""
        for i in range(2 ** self.length):
            if bit(i, bit_index) == bit_value:
                # take the 'sister' bit combination and add its
                # probability to this one
                if bit_value:
                    sister = i & (~ 1<<bit_index)
                else:
                    sister = i | bit_index
                total_prob = self.contents[i] **2 + self.contents[sister]**2
                self.contents[i] = math.sqrt(total_prob)
                self.contents[sister] = 0


#############################################################

import math
st = 1/math.sqrt(2)

Hadamard = Gate(1, [[st, st],
                    [st, -st]])

CNOT = Gate(2, [[1, 0, 0, 0],
                [0, 1, 0, 0],
                [0, 0, 0, 1],
                [0, 0, 1, 0]])

CV = Gate(2, [[1, 0, 0, 0],
              [0, 1, 0, 0],
              [0, 0, 1, 0],
              [0, 0, 0, 1j]])

if __name__ == '__main__':
    # just testing stuff
    r = Register(2)
    Hadamard.apply(r, [0])
    print r.contents
    CNOT.apply(r,[1,0])
    print r.contents
    r.set(0, 1)
    print r.contents