ControlledBlockGate.java

/*-
 * #%L
 * Strange
 * %%
 * Copyright (C) 2020 Johan Vos
 * %%
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 
 * 3. Neither the name of the Johan Vos nor the names of its contributors
 *    may be used to endorse or promote products derived from this software without
 *    specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 * #L%
 */
package org.redfx.strange;

import org.redfx.strange.gate.PermutationGate;
import org.redfx.strange.local.Computations;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

/**
 *
 * A Gate describes an operation on one or more qubits.
 *
 * @author johan
 * @param <T> type of the Block
 * @version $Id: $Id
 */
public class ControlledBlockGate<T> extends BlockGate {

    private int control;
    private int size;
    private int high = -1;
    private int haq = -1;
    int low = 0;
    private Complex[][] matrix = null;
    
    /**
     * <p>Constructor for ControlledBlockGate.</p>
     */
    protected ControlledBlockGate() {
    }
    
    /**
     * Create a controlled blockgate
     *
     * @param bg the block gate
     * @param idx the start-index of the block gate
     * @param control the index of the control qubit
     */
    public ControlledBlockGate(BlockGate bg, int idx, int control) {
        this (bg.getBlock(), idx, control);
    }
    
    /**
     * Create a block
     *
     * @param block a {@link org.redfx.strange.Block} object
     * @param idx a int
     * @param control the control qubit
     */
    public ControlledBlockGate (Block block, int idx, int control) {
        super(block, idx);
        this.control = control;
        if (control > idx) {
            this.haq = idx + block.getNQubits();
        } else {
            this.haq = idx+block.getNQubits() -1;
        }
    }

    /** {@inheritDoc} */
    @Override
    public List<Integer> getAffectedQubitIndexes() {
        ArrayList answer = new ArrayList(super.getAffectedQubitIndexes());
        answer.add(control);
        return answer;
    }

    /** {@inheritDoc} */
    @Override
    public int getHighestAffectedQubitIndex() {
        if (high < 0) calculateHighLow();
        return this.haq;
    }

    /** {@inheritDoc} */
    @Override
    public String getCaption() {
        return "CB";
    }

    /** {@inheritDoc} */
    @Override
    public String getName() {
        return "CBlockGate";
    }

    /** {@inheritDoc} */
    @Override
    public String getGroup() {
        return "CBlockGroup";
    }
    
    /**
     * <p>getControlQubit.</p>
     *
     * @return a int
     */
    public int getControlQubit() {
        return this.control;
    }

    /**
     * <p>calculateHighLow.</p>
     */
    public void calculateHighLow() {
        this.high = control;
        int gap = control - idx;
        int bs = block.getNQubits();
        low = 0;
         if (control > idx) {
             low =idx;
                if (gap < bs) {
                    throw new IllegalArgumentException("Can't have control at " + control + " for gate with size " + bs + " starting at " + idx);
                }
                if (gap > bs) {
                    high = control;
                }
            } else {
                low = control;
                high = idx + bs - 1;
         }
         size = high - low + 1;
    }
    
    /**
     * <p>Getter for the field <code>low</code>.</p>
     *
     * @return a int
     */
    public int getLow() {
        return this.low;
    }
    
    /**
     * <p>correctHigh.</p>
     *
     * @param h a int
     */
    public void correctHigh(int h) {
        this.high = h;
    }
    
    /** {@inheritDoc} */
    @Override
    public Complex[][] getMatrix() {
        return getMatrix(null);
    }

    /** {@inheritDoc} */
    @Override
    public Complex[][] getMatrix(QuantumExecutionEnvironment qee) {
        if (matrix == null) {
            int low = 0;
            this.high = control;
            this.size = super.getSize() + 1;
            int gap = control - idx;
            List<PermutationGate> perm = new LinkedList<>();
            int bs = block.getNQubits();
            if (control > idx) {
                if (gap < bs) {
                    throw new IllegalArgumentException("Can't have control at " + control + " for gate with size " + bs + " starting at " + idx);
                }
                low = idx;
                if (gap > bs) {
                    high = control;
                    size = high - low + 1;
                    PermutationGate pg = new PermutationGate(control-low, control-low - gap + bs, size);
                    perm.add(pg);
                }
            } else {
                low = control;
                high = idx + bs - 1;
                size = high - low + 1;
                for (int i = 0; i < size - 1; i++) {
                    PermutationGate pg = new PermutationGate(i, i + 1, size);
                    perm.add(0,pg);
                }
            }
            Complex[][] part = block.getMatrix(qee);
          
            int dim = part.length;
            matrix = Computations.createIdentity(2 * dim);
            for (int i = 0; i < dim; i++) {
                for (int j = 0; j < dim; j++) {
                    matrix[i + dim][j + dim] = part[i][j];
                }
            }
        } else {
            System.err.println("Matrix was cached");
        }
        return matrix;
    }
    
    /** {@inheritDoc} */
    @Override
    public boolean hasOptimization() {
        return true;
    }

    /** {@inheritDoc} */
    @Override
    public Complex[] applyOptimize(Complex[] v) {
        int size = v.length;
        Complex[] answer = new Complex[size];
        int dim = size / 2;
        Complex[] oldv = new Complex[dim];
        for (int i = 0; i < dim; i++) {
            oldv[i] = v[i + dim];
        }
        Complex[] p2 = block.applyOptimize(oldv, inverse);
        for (int i = 0; i < dim; i++) {
            answer[i] = v[i];
            answer[dim + i] = p2[i];
        }
        return answer;
    }

    /**
     * <p>Getter for the field <code>size</code>.</p>
     *
     * @return a int
     */
    public int getSize() {
        return block.getNQubits()+1;
    }
    
    /** {@inheritDoc} */
    @Override public String toString() {
        return "ControlledGate for "+super.toString();
    }
    
}