Program.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.Cnot;
import org.redfx.strange.gate.Hadamard;
import org.redfx.strange.gate.Measurement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
/**
*
* A Quantum Program.
* A Program contains a list of <code>Step</code>s that are executed sequentially
* by a QuantumExecutionEnvironment.
*
* @author johan
* @version $Id: $Id
*/
public class Program {
private final int numberQubits;
private Result result;
private double[] initAlpha;
private final ArrayList<Step> steps = new ArrayList<>();
// cache decomposedSteps
private List<Step> decomposedSteps = null;
/**
* Create a Quantum Program and indicate how many qubits will be involved.
* By default, all qubits are initialized to the |0 > state. Steps can
* be added via this constructor, but they can also be added later by calling
* the {@link addStep} or {@link addSteps} methods.
*
* @param nQubits the amount of qubits that will be used in this program
* @param moreSteps steps to add to the program
*/
public Program(int nQubits, Step... moreSteps) {
this.numberQubits = nQubits;
this.initAlpha = new double[numberQubits];
Arrays.fill(initAlpha, 1d);
addSteps(moreSteps);
}
/**
* Initialize this qubit. The qubit will be in a state
* \psi = \alpha |0 > + \beta |1 > .
* Since \alpha^2 + \beta^2 should equals 1, only
* \alpha is required.
*
* @param idx the index of the qubit to be initialized
* @param alpha the alpha value of the qubit state.
* @throws java.lang.IllegalArgumentException in case the index of the qubit is higher than the amount of qubits -1 .
*/
public void initializeQubit(int idx, double alpha) {
if (idx >= numberQubits) {
throw new IllegalArgumentException("Can not initialize qubit "+
idx+" since we have only "+numberQubits+" qubits.");
}
this.initAlpha[idx] = alpha;
}
/**
* This method allows to set the initial values of the probabilities of the qubits in this
* program. By default, all qubits are in the |0 > state when a Program is created.
* However, for testing or demoes, it sometimes makes sense to show the behavior of an
* application that starts with non-zero qubits.
*
* @return an array of the initial probabilities that the qubits in this program are supposed to have.
*/
public double[] getInitialAlphas() {
return this.initAlpha;
}
/**
* Adds a {@link Step} containing zero or more gates to the current program.
* In case the {@link Step} contains an operation that would put a measured qubit into a potential superposition
* again, an IllegalArgumentException is thrown.
*
* @param step the {@link Step} to be added to the program
*/
public void addStep(Step step) {
if (!ensureMeasuresafe( Objects.requireNonNull(step)) ) {
throw new IllegalArgumentException ("Adding a superposition step to a measured qubit");
}
step.setIndex(steps.size());
step.setProgram(this);
steps.add(step);
this.decomposedSteps = null;
}
/**
* Adds multiple steps with one or more gates to the existing program.
* In case the Step contains an operation that would put a measured qubit into a potential superposition
* again, an IllegalArgumentException is thrown.
*
* @param moreSteps steps to be added to the program
*/
public void addSteps(Step... moreSteps) {
for ( Step step: moreSteps) {
addStep(step);
}
}
private boolean ensureMeasuresafe(Step newStep) {
// determine which qubits might get superpositioned
List<Integer> mainQubits = new ArrayList<>();
for (Gate g : newStep.getGates()) {
if (g instanceof Hadamard) {
mainQubits.add(g.getMainQubitIndex());
} else if (g instanceof Cnot) {
mainQubits.add(((Cnot) g).getSecondQubitIndex());
}
}
for (Step step : this.getSteps()) {
boolean match = step.getGates().stream().filter(g -> g instanceof Measurement)
.map(Gate::getMainQubitIndex).anyMatch(mainQubits::contains);
if (match) return false;
}
;
return true;
}
/**
* Return the list of {@link Step}s in this program. Steps are added either via the
* constructor or via the {@link addStep} or {@link addSteps} methods.
*
* @return a list of {@link Step}s.
*/
public List<Step> getSteps() {
return this.steps;
}
/**
* <p>Getter for the field <code>decomposedSteps</code>.</p>
*
* @return a {@link java.util.List} object
*/
@Deprecated
public List<Step> getDecomposedSteps () {
return this.decomposedSteps;
}
/**
* <p>Setter for the field <code>decomposedSteps</code>.</p>
*
* @param ds a {@link java.util.List} object
*/
@Deprecated
public void setDecomposedSteps(List<Step> ds) {
this.decomposedSteps = ds;
}
/**
* Return the number of @{link Qubit}s in this program.
*
* @return an int containing the number of qubits in this program.
*/
public int getNumberQubits() {
return this.numberQubits;
}
/**
* Sets the {@link Result} of the execution of the program. This method is
* invoked by the {@link QuantumExecutionEnvironment} when it has executed
* this program. It should not be called by the developer.
*
* @param r a {@link org.redfx.strange.Result} object
*/
public void setResult(Result r) {
this.result = r;
}
/**
* Returns the {@link Result} object of a program, after it has been
* executed by a {@link QuantumExecutionEnvironment}.
*
* @return a {@link org.redfx.strange.Result} object
*/
public Result getResult() {
return this.result;
}
/**
* Print info about this program to stdout
*/
public void printInfo() {
System.out.println("Info about Quantum Program");
System.out.println("==========================");
System.out.println("Number of qubits = "+numberQubits+", number of steps = "+steps.size());
steps.forEach(step -> {
System.out.println("Step: "+step.getGates());
});
System.out.println("==========================");
}
}