From 617096b772fd566edbe8106be9f8526ae583e42e Mon Sep 17 00:00:00 2001 From: Tsvetelin Kostadinov Date: Fri, 6 Dec 2019 09:38:22 +0200 Subject: [PATCH 1/9] Final commit for version 1.1 --- .classpath | 2 +- .gitignore | 1 + .../java/com/simulationQ/util/math/QMath.java | 4 +- .../util/math/matrices/Matrix.java | 64 ++++++----- .../util/math/matrices/MatrixOperations.java | 101 +++++++++++------- .../matrices/vectors/VectorOperations.java | 27 +++++ 6 files changed, 133 insertions(+), 66 deletions(-) diff --git a/.classpath b/.classpath index 9b28bc4..1158cfa 100644 --- a/.classpath +++ b/.classpath @@ -6,7 +6,7 @@ - + diff --git a/.gitignore b/.gitignore index 408e203..d68ae4e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /bin /test_results /.settings +/build \ No newline at end of file diff --git a/src/main/java/com/simulationQ/util/math/QMath.java b/src/main/java/com/simulationQ/util/math/QMath.java index c8d2530..aa33991 100644 --- a/src/main/java/com/simulationQ/util/math/QMath.java +++ b/src/main/java/com/simulationQ/util/math/QMath.java @@ -85,8 +85,8 @@ private Constants ( BigDecimal a ) * @param b * @return true if the linear combination was 1, and false otherwise */ - public static boolean checkLinearCombinationEqualToOne ( BigDecimal a , - BigDecimal b ) + public static boolean checkLinearCombinationEqualToOne ( final BigDecimal a , + final BigDecimal b ) { return a.pow( 2 ) .add( b.pow( 2 ) ) diff --git a/src/main/java/com/simulationQ/util/math/matrices/Matrix.java b/src/main/java/com/simulationQ/util/math/matrices/Matrix.java index 422a947..06c89be 100644 --- a/src/main/java/com/simulationQ/util/math/matrices/Matrix.java +++ b/src/main/java/com/simulationQ/util/math/matrices/Matrix.java @@ -43,7 +43,7 @@ public class Matrix implements MatrixOperations public Matrix ( final ComplexNumber [] [] matrix ) { super(); - if ( !MatrixOperations.isMatrixRectangular( matrix ) ) + if ( !MatrixOperations.isMatrixSquare( matrix ) ) throw new IllegalArgumentException( "The matrix HAS to be rectangular" ); this.matrix = matrix; this.rows = matrix.length; @@ -51,6 +51,8 @@ public Matrix ( final ComplexNumber [] [] matrix ) } /** + * + * Constructs an empty matrix * * @param rows * @param colons @@ -65,6 +67,9 @@ public Matrix ( final int rows , final int colons ) } /** + * + * Generates a matrix -g with the specified dimensions, + * that satisfies a + g = a * * @param rows * @param colons @@ -86,8 +91,17 @@ public Matrix ( final int rows , final int colons ) } + /** + * + * Generates a matrix -g with the specified dimensions, + * that satisfies a * g = a + * + * @param rows + * @param colons + * @return the multiplicative identity. + */ public static final ComplexNumber [] [] multiplicativeIdentity ( final int rows , - final int colons ) + final int colons ) { final ComplexNumber [] [] res = new ComplexNumber[rows][colons]; @@ -95,13 +109,14 @@ public Matrix ( final int rows , final int colons ) { for ( int j = 0 ; j < colons ; j++ ) { - if( i==j ) + if ( i == j ) { res[i][j] = ComplexNumber.REAL_UNIT; - }else { + } else + { res[i][j] = ComplexNumber.ORIGIN; } - + } } return res; @@ -141,9 +156,9 @@ public int getColons () public ComplexNumber getAt ( final int row , final int colon ) { if ( row < 0 || row >= this.rows ) - throw new IndexOutOfBoundsException( row ); + throw new IndexOutOfBoundsException( "" + row ); if ( colon < 0 || colon >= this.colons ) - throw new IndexOutOfBoundsException( colon ); + throw new IndexOutOfBoundsException( "" + colon ); return matrix[row][colon]; } @@ -161,9 +176,9 @@ public void setAt ( final int row , final ComplexNumber value ) { if ( row < 0 || row >= this.rows ) - throw new IndexOutOfBoundsException( row ); + throw new IndexOutOfBoundsException( "" + row ); if ( colon < 0 || colon >= this.colons ) - throw new IndexOutOfBoundsException( colon ); + throw new IndexOutOfBoundsException( "" + colon ); this.matrix[row][colon] = value; } @@ -189,22 +204,20 @@ public Matrix add ( final Matrix a ) @Override public Matrix multiply ( final Matrix a ) { - -// System.out.println( "Multiplication." ); - + + // System.out.println( "Multiplication." ); + final Matrix res = new Matrix( this.getRows() , a.getColons() ); - -// System.out.println( "THIS: " + this ); -// System.out.println( this.rows ); -// System.out.println( "* by : "+ a ); -// System.out.println( a.getColons() ); - + + // System.out.println( "THIS: " + this ); + // System.out.println( this.rows ); + // System.out.println( "* by : "+ a ); + // System.out.println( a.getColons() ); + if ( a.getColons() != this.getRows() ) - { - throw new IllegalArgumentException( "Illegal matrix dimensions." ); - } - -// System.out.println( res ); + { throw new IllegalArgumentException( "Illegal matrix dimensions." ); } + + // System.out.println( res ); for ( int i = 0 ; i < res.getRows() ; i++ ) { @@ -277,9 +290,10 @@ public Matrix multiplyWithScalar ( ComplexNumber a ) { for ( int j = 0 ; j < this.getColons() ; j++ ) { - res.setAt( i , j , + res.setAt( i , + j , this.getAt( i , j ) - .multiply( a ) ); + .multiply( a ) ); } } return res; diff --git a/src/main/java/com/simulationQ/util/math/matrices/MatrixOperations.java b/src/main/java/com/simulationQ/util/math/matrices/MatrixOperations.java index 73dcb6a..772a010 100644 --- a/src/main/java/com/simulationQ/util/math/matrices/MatrixOperations.java +++ b/src/main/java/com/simulationQ/util/math/matrices/MatrixOperations.java @@ -21,18 +21,44 @@ public interface MatrixOperations extends ArithmeticOperations< Matrix > { + /** + * Multiplies the scalar with the matrix + * + * @param a + * - the scalar + * @return this*a + */ public Matrix multiplyWithScalar ( final ComplexNumber a ); + /** + * Multiplies the scalar with the matrix + * + * @param a + * - the scalar + * @param b + * - the matrix + * @return a*b + */ public static Matrix multiplyWithScalar ( final ComplexNumber a , final Matrix b ) { return b.multiplyWithScalar( a ); } + + /** + * Applies the trifunction to each element of the matrix + * @param mapper - a trifunction receiving the x, y and value and producing the new value + * @return + */ + public Matrix map ( final TriFunction< Integer , Integer , ComplexNumber , ComplexNumber > mapper ); - public Matrix map ( TriFunction< Integer , Integer , ComplexNumber , ComplexNumber > mapper ); - - public static Matrix map ( TriFunction< Integer , Integer , ComplexNumber , ComplexNumber > mapper , - Matrix a ) + /** + * Applies the trifunction to each element of the matrix + * @param mapper - a trifunction receiving the x, y and value and producing the new value + * @return + */ + public static Matrix map ( final TriFunction< Integer , Integer , ComplexNumber , ComplexNumber > mapper , + final Matrix a ) { return a.map( mapper ); } @@ -43,7 +69,7 @@ public static Matrix map ( TriFunction< Integer , Integer , ComplexNumber , Comp * @return true if the number of rows equals the number of colons, false * otherwise */ - public static < T > boolean isMatrixRectangular ( final T [] [] matrix ) + public static < T > boolean isMatrixSquare ( final T [] [] matrix ) { final int checkAgainst = matrix[0].length; @@ -99,13 +125,11 @@ public static Vector multiply ( final Matrix a , final Vector b ) ComplexNumber [] row = a.getMatrix()[i]; ComplexNumber sumOfRow = ComplexNumber.ORIGIN; - - + for ( int j = 0 ; j < b.size() ; j++ ) { sumOfRow = sumOfRow.add( row[j].multiply( b.getAt( j ) ) ); } - res.setAt( i , sumOfRow ); @@ -115,35 +139,36 @@ public static Vector multiply ( final Matrix a , final Vector b ) } -// public static void main ( String [] args ) -// { -// Matrix m = new Matrix( new ComplexNumber[][] { -// { ComplexNumber.REAL_UNIT, ComplexNumber.REAL_UNIT.negate(), -// ComplexNumber.REAL_UNIT.add( ComplexNumber.REAL_UNIT ) }, // 1, -// // -1, -// // 2 -// { ComplexNumber.ORIGIN, -// ComplexNumber.REAL_UNIT.add( ComplexNumber.REAL_UNIT.add( ComplexNumber.REAL_UNIT ) ) -// .negate(), -// ComplexNumber.REAL_UNIT } // 0, -3, 1 -// } ); -// -// System.out.println( m ); -// -// Vector v = new Vector( new ComplexNumber[] { -// ComplexNumber.REAL_UNIT.add( ComplexNumber.REAL_UNIT ), -// ComplexNumber.REAL_UNIT, -// ComplexNumber.ORIGIN -// } ); -// -// System.out.println( ); -// -// System.out.println( v ); -// -// System.out.println( ); -// -// System.out.println( MatrixOperations.multiply( m , v ) ); -// -// } + // public static void main ( String [] args ) + // { + // Matrix m = new Matrix( new ComplexNumber[][] { + // { ComplexNumber.REAL_UNIT, ComplexNumber.REAL_UNIT.negate(), + // ComplexNumber.REAL_UNIT.add( ComplexNumber.REAL_UNIT ) }, // 1, + // // -1, + // // 2 + // { ComplexNumber.ORIGIN, + // ComplexNumber.REAL_UNIT.add( ComplexNumber.REAL_UNIT.add( + // ComplexNumber.REAL_UNIT ) ) + // .negate(), + // ComplexNumber.REAL_UNIT } // 0, -3, 1 + // } ); + // + // System.out.println( m ); + // + // Vector v = new Vector( new ComplexNumber[] { + // ComplexNumber.REAL_UNIT.add( ComplexNumber.REAL_UNIT ), + // ComplexNumber.REAL_UNIT, + // ComplexNumber.ORIGIN + // } ); + // + // System.out.println( ); + // + // System.out.println( v ); + // + // System.out.println( ); + // + // System.out.println( MatrixOperations.multiply( m , v ) ); + // + // } } diff --git a/src/main/java/com/simulationQ/util/math/matrices/vectors/VectorOperations.java b/src/main/java/com/simulationQ/util/math/matrices/vectors/VectorOperations.java index 59ab1c0..add878d 100644 --- a/src/main/java/com/simulationQ/util/math/matrices/vectors/VectorOperations.java +++ b/src/main/java/com/simulationQ/util/math/matrices/vectors/VectorOperations.java @@ -15,14 +15,41 @@ */ public interface VectorOperations extends MatrixOperations { + /** + * + * Multiplies the vectors in a specific way, not sure of the term + * + * @param a + * @param b + * @return + */ public static Vector tensorProduct( final Vector a , final Vector b) { return a.tensorProduct(b); } + /** + * + * Makes the tensor product of this and a + * + * @param a + * @return + */ public Vector tensorProduct( final Vector a); + /** + * + * Pretty much like List.sublist + * + * @param indexFrom + * @param indexTo + * @return + */ public Vector get( final int indexFrom, final int indexTo ); + /** + * + * @return size of the vector + */ public int size(); } From 42dba8ce38fadcec5aa56cd11289e897c5aec7fd Mon Sep 17 00:00:00 2001 From: Tsvetelin Kostadinov Date: Sun, 8 Dec 2019 13:16:23 +0200 Subject: [PATCH 2/9] Namespace separation between the simulation, GUI and engine --- src/main/java/com/simulationQ/Main.java | 4 - .../java/com/simulationQ/gui/MainWindow.java | 119 ++++++++++++------ .../java/com/simulationQ/gui/icon/favicon.ico | Bin 99678 -> 0 bytes .../java/com/simulationQ/gui/icon/logo.png | Bin 58627 -> 0 bytes .../com/simulationQ/gui/package-info.java | 2 +- .../computation/gates/QGate.java | 12 +- .../computation/gates/impl/Hadamard.java | 10 +- .../computation/gates/impl/NOT.java | 10 +- .../computation/gates/impl/PauliY.java | 10 +- .../computation/gates/impl/PauliZ.java | 10 +- .../computation/gates/impl/package-info.java | 2 +- .../computation/gates/package-info.java | 2 +- .../computation/package-info.java | 2 +- .../computation/qubits/QCollapser.java | 4 +- .../computation/qubits/Qubit.java | 11 +- .../computation/qubits/package-info.java | 2 +- .../qubits/register/QRegister.java | 6 +- .../qubits/register/package-info.java | 2 +- .../icon => simulation}/package-info.java | 4 +- .../util/math/ArithmeticOperations.java | 2 +- .../{ => simulation}/util/math/QMath.java | 2 +- .../math/complexNumbers/ComplexNumber.java | 4 +- .../util/math/complexNumbers/Operations.java | 4 +- .../math/complexNumbers/package-info.java | 2 +- .../util/math/functional/TriFunction.java | 2 +- .../util/math/functional/package-info.java | 2 +- .../util/math/matrices/Matrix.java | 7 +- .../util/math/matrices/MatrixOperations.java | 12 +- .../util/math/matrices/package-info.java | 2 +- .../util/math/matrices/vectors/Vector.java | 9 +- .../matrices/vectors/VectorOperations.java | 4 +- .../math/matrices/vectors/package-info.java | 2 +- .../util/math/package-info.java | 2 +- .../math/matrices/MatrixOperationsTest.java | 7 +- 34 files changed, 152 insertions(+), 123 deletions(-) delete mode 100644 src/main/java/com/simulationQ/gui/icon/favicon.ico delete mode 100644 src/main/java/com/simulationQ/gui/icon/logo.png rename src/main/java/com/simulationQ/{ => simulation}/computation/gates/QGate.java (91%) rename src/main/java/com/simulationQ/{ => simulation}/computation/gates/impl/Hadamard.java (81%) rename src/main/java/com/simulationQ/{ => simulation}/computation/gates/impl/NOT.java (76%) rename src/main/java/com/simulationQ/{ => simulation}/computation/gates/impl/PauliY.java (73%) rename src/main/java/com/simulationQ/{ => simulation}/computation/gates/impl/PauliZ.java (73%) rename src/main/java/com/simulationQ/{ => simulation}/computation/gates/impl/package-info.java (63%) rename src/main/java/com/simulationQ/{ => simulation}/computation/gates/package-info.java (65%) rename src/main/java/com/simulationQ/{ => simulation}/computation/package-info.java (75%) rename src/main/java/com/simulationQ/{ => simulation}/computation/qubits/QCollapser.java (96%) rename src/main/java/com/simulationQ/{ => simulation}/computation/qubits/Qubit.java (91%) rename src/main/java/com/simulationQ/{ => simulation}/computation/qubits/package-info.java (65%) rename src/main/java/com/simulationQ/{ => simulation}/computation/qubits/register/QRegister.java (87%) rename src/main/java/com/simulationQ/{ => simulation}/computation/qubits/register/package-info.java (61%) rename src/main/java/com/simulationQ/{gui/icon => simulation}/package-info.java (57%) rename src/main/java/com/simulationQ/{ => simulation}/util/math/ArithmeticOperations.java (98%) rename src/main/java/com/simulationQ/{ => simulation}/util/math/QMath.java (97%) rename src/main/java/com/simulationQ/{ => simulation}/util/math/complexNumbers/ComplexNumber.java (98%) rename src/main/java/com/simulationQ/{ => simulation}/util/math/complexNumbers/Operations.java (98%) rename src/main/java/com/simulationQ/{ => simulation}/util/math/complexNumbers/package-info.java (68%) rename src/main/java/com/simulationQ/{ => simulation}/util/math/functional/TriFunction.java (90%) rename src/main/java/com/simulationQ/{ => simulation}/util/math/functional/package-info.java (64%) rename src/main/java/com/simulationQ/{ => simulation}/util/math/matrices/Matrix.java (97%) rename src/main/java/com/simulationQ/{ => simulation}/util/math/matrices/MatrixOperations.java (93%) rename src/main/java/com/simulationQ/{ => simulation}/util/math/matrices/package-info.java (72%) rename src/main/java/com/simulationQ/{ => simulation}/util/math/matrices/vectors/Vector.java (93%) rename src/main/java/com/simulationQ/{ => simulation}/util/math/matrices/vectors/VectorOperations.java (87%) rename src/main/java/com/simulationQ/{ => simulation}/util/math/matrices/vectors/package-info.java (69%) rename src/main/java/com/simulationQ/{ => simulation}/util/math/package-info.java (77%) diff --git a/src/main/java/com/simulationQ/Main.java b/src/main/java/com/simulationQ/Main.java index 0895222..510507c 100644 --- a/src/main/java/com/simulationQ/Main.java +++ b/src/main/java/com/simulationQ/Main.java @@ -4,8 +4,6 @@ */ package com.simulationQ; -import com.simulationQ.gui.MainWindow; - /** * @author Tsvetelin * @@ -24,9 +22,7 @@ public Main () */ public static void main ( String [] args ) { - Thread main = new Thread( new MainWindow() ); - main.start(); } } diff --git a/src/main/java/com/simulationQ/gui/MainWindow.java b/src/main/java/com/simulationQ/gui/MainWindow.java index c878647..bf7c3f1 100644 --- a/src/main/java/com/simulationQ/gui/MainWindow.java +++ b/src/main/java/com/simulationQ/gui/MainWindow.java @@ -2,43 +2,36 @@ * 27/11/2019 16:07:58 * MainWindow.java created by Tsvetelin */ -package com.simulationQ.gui; +package com.simulationQ.GUI; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.EventQueue; -import java.awt.Graphics; -import java.awt.Graphics2D; +import java.awt.Toolkit; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import javax.swing.JButton; +import javax.swing.JComboBox; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextArea; import javax.swing.border.EmptyBorder; -import com.simulationQ.computation.gates.QGate; -import com.simulationQ.computation.gates.impl.Hadamard; -import com.simulationQ.computation.gates.impl.NOT; -import com.simulationQ.computation.gates.impl.PauliY; -import com.simulationQ.computation.gates.impl.PauliZ; -import com.simulationQ.computation.qubits.QCollapser; -import com.simulationQ.computation.qubits.Qubit; -import com.simulationQ.computation.qubits.register.QRegister; - -import java.awt.Toolkit; -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; -import java.awt.geom.Line2D; - -import javax.swing.JButton; -import javax.swing.JComboBox; +import com.simulationQ.simulation.computation.gates.QGate; +import com.simulationQ.simulation.computation.gates.impl.Hadamard; +import com.simulationQ.simulation.computation.gates.impl.NOT; +import com.simulationQ.simulation.computation.gates.impl.PauliY; +import com.simulationQ.simulation.computation.gates.impl.PauliZ; +import com.simulationQ.simulation.computation.qubits.QCollapser; +import com.simulationQ.simulation.computation.qubits.Qubit; +import com.simulationQ.simulation.computation.qubits.register.QRegister; /** * @author Tsvetelin * */ +@Deprecated public class MainWindow extends JFrame implements Runnable { @@ -56,6 +49,8 @@ public class MainWindow extends JFrame implements Runnable private JComboBox< String > gate1; private JComboBox< String > gate2; + + private JComboBox< String > gate3; private QGate hadamard = new Hadamard(); @@ -113,23 +108,18 @@ public MainWindow () btnCollapse.addMouseListener( new MouseListener() { - @Override public void mouseReleased ( MouseEvent e ) {} - @Override public void mousePressed ( MouseEvent e ) {} - @Override public void mouseExited ( MouseEvent e ) {} - @Override public void mouseEntered ( MouseEvent e ) {} - @Override public void mouseClicked ( MouseEvent e ) { updateCollapseInfo(); @@ -139,7 +129,7 @@ public void mouseClicked ( MouseEvent e ) contentPane.add( btnCollapse ); lblRes = new JLabel( Qubit.QUBIT_ON.toString() ); - lblRes.setBounds( 300 , 40 , 500 , 20 ); + lblRes.setBounds( 440 , 40 , 500 , 20 ); lblRes.setVisible( false ); contentPane.add( lblRes ); @@ -149,23 +139,18 @@ public void mouseClicked ( MouseEvent e ) btnCollapseWithoutFinalResult.addMouseListener( new MouseListener() { - @Override public void mouseReleased ( MouseEvent e ) {} - @Override public void mousePressed ( MouseEvent e ) {} - @Override public void mouseExited ( MouseEvent e ) {} - @Override public void mouseEntered ( MouseEvent e ) {} - @Override public void mouseClicked ( MouseEvent e ) { updateIntospectionInfo(); @@ -195,16 +180,27 @@ public void mouseClicked ( MouseEvent e ) gate2.addItem( "Pauli-Z" ); contentPane.add( gate2 ); - } + + gate3 = new JComboBox< String >(); + gate3.setBounds( 310 , 40 , 100 , 20 ); - public void paint ( Graphics gp ) - { - super.paint( gp ); - Graphics2D graphics = ( Graphics2D ) gp; - // Line2D line = new Line2D.Float( 40 , 80 , 300 , 80 ); - // graphics.draw( line ); + gate3.addItem( "None" ); + gate3.addItem( "Hadamard" ); + gate3.addItem( "Pauli-X/NOT" ); + gate3.addItem( "Pauli-Y" ); + gate3.addItem( "Pauli-Z" ); + + contentPane.add( gate3 ); } +// public void paint ( Graphics gp ) +// { +// super.paint( gp ); +// Graphics2D graphics = ( Graphics2D ) gp; +// // Line2D line = new Line2D.Float( 40 , 80 , 300 , 80 ); +// // graphics.draw( line ); +// } + private void updateCollapseInfo () { lblRes.setVisible( true ); @@ -253,6 +249,29 @@ private void updateCollapseInfo () default : break; } + + inputReg = new QRegister( new Qubit[] { input } ); + + switch ( gate3.getSelectedIndex() ) + { + case 1 : + input = hadamard.apply( inputReg ).getQubit( 0 ); + break; + case 2 : + input = not.apply( inputReg ).getQubit( 0 ); + break; + case 3 : + input = pauliY.apply( inputReg ).getQubit( 0 ); + break; + case 4 : + input = pauliZ.apply( inputReg ).getQubit( 0 ); + break; + + default : + break; + } +// +// inputReg = new QRegister( new Qubit[] { input } ); Qubit res = QCollapser.collapse( Long.parseLong( txtCollapseCounting.getText() ) , input ); @@ -312,6 +331,26 @@ private void updateIntospectionInfo () default : break; } + inputReg = new QRegister( new Qubit[] { input } ); + + switch ( gate3.getSelectedIndex() ) + { + case 1 : + input = hadamard.apply( inputReg ).getQubit( 0 ); + break; + case 2 : + input = not.apply( inputReg ).getQubit( 0 ); + break; + case 3 : + input = pauliY.apply( inputReg ).getQubit( 0 ); + break; + case 4 : + input = pauliZ.apply( inputReg ).getQubit( 0 ); + break; + + default : + break; + } lblRes.setText( input.toString() ); diff --git a/src/main/java/com/simulationQ/gui/icon/favicon.ico b/src/main/java/com/simulationQ/gui/icon/favicon.ico deleted file mode 100644 index 7dbd3f0d77330fbe02a7f2e51bf2afb15d56763f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 99678 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 diff --git a/src/main/java/com/simulationQ/gui/icon/logo.png b/src/main/java/com/simulationQ/gui/icon/logo.png deleted file mode 100644 index 1828883f288c9fc2c82c7176a824e3496db0c2cc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 58627 zcmd2?^Fz$@q@^XkbazVE!OHI`%U=pMCG_%vV8gD`6d0{@sO?Wo<4jO`d_#kRFW~{g?GG?m$KB_2^kOqh=%KqOSPj*&@ey z)p$sp+7`G#<%dS`!R)u7Fyl9P3lswhWkmjH{zQ_JyRNt8(`Cm#l!4;>URyUJqZCds zt4jf&t7Wr_)|p$_=>gA*z;?s%jgQk1_s*T~iq_ro*49?H0T%@UHuSvXp%@5-qZ+Bt zw*mkA2>#Lt1o*#4^#AW4t)u4hBL)gzowPr;A-8L2=WKIysOWVvtpVsmo zErbXt5Ma9zIsqsM(%&7;Z$C0}0|Vi!R|fwk`fM$g?^dRp8YPW_C;{|P`C-Lx7~mxKW~IN9U^`oUM9aiu&gwNv9^Lwh@^0oc$EYh;1H01*K;VK9n+ zgtB@sgLj*Yi=u@a#m>(<6a-oR$6d*dV(vHQeSjkjK^Rb{UKB5L=-dS`Dh$Ou@(cwp zmB4fYEQ~uOVPMaJ!oRBYueYKRs<&zns<#_)^(kRG(>5I!ztj+#fxQ`5%Ht}p!9Ow_ zMXsksk!@K~LpKcm<5o99aqD(aV`95KN4hoP2cnW*}P5W61iL_{IX zfdY;EClQK8_91$M6aIe;c&6uIf+LQ9lXx*?hekm5zinRAXN0s^=a>duvc_Ee5HRR$K8yya1^zvp?GbJO*p_Y2M5)F5_J31d6+{i36a`SC{s$8})43UhT2>{B>UNZg z&U9>soTHRL=|y!Vs@ZAa*nNXCROS%PF%;)lE`beUZhdrN`k_~}C>6#!?`}WuK=sB% zy-JTTDt3%H2s=UmCl$C2ut!ho^0{p&!}TyacIFQDn?Nbz5kC`J2DTFWoRUsD_BN6I zJW3igJ&beB!A{DBB-JR}!Qs~(LR*bABHmkSXB@4F2;FG*58asPLh;fuSuE=*WP4jB z&B9ufW<^E*bu=X2JnUfXPU-HtJM8GXvp{K_8%`X79gqoEluF@#1*CAF0YZV1;p#u0 zGm2e4>?jR33)dt>SrT9oP2rChZxW;19kc}G|)Hw3op$JoMTto{O zVsD$`mXG>NMJ0e4M9OS~?H{}You5|6Gra3fCo zji%QRQQ*_Lnf8QVD*SmV{B|hW+cC+-qZzgAW+Ad4l(FMXu=)HoGnBEbMeR7Z0Vf39 zxZ7oh#Dl9A2Ge`my`@tNp#lJ51_c!lZ-sX+g>8mBeGo}5UU&9w`*77R#<<>I#u#`t zz`-rD>bh3%r1c}Ew)0Es!$?o)LZAow$#3qIrwv1tA=toGbvrLM`v}KV_FfNpHHi|* z)VgrQFyyco&sJfvFL}lkOnt-n>v$V;Y-<;vXUO(n2oDF}R9xUS2d#IY|1(>B6u93g z*fPB+ZCwcQvQs^q^xgJR3Vc>kuU;o&|D*X^psiE(%zU1)l4c_O%yi>>@<#|^`5%td z(c|!3u?xc)(_f!Ar1BKA4I+}zb*lYWdh?~F^^e|EbdsNQ`Du%Onwjgo-R>Upg(DEwFPR%GZ{K zMJ_)S|2fryQNzu(T#nC9{E)~$8r5T8zwLkh)_E(FAEv!_GbJ^5dlZ>}8FlClvvw2A z&CPlh*!Hwv;V;yHh0-6@4(rFLf9C3jHQIV8Ff3cyl%w@xqpQ_7E9?9s5zlif4G`ir zm*?rGu-DGlMO@AQlz7`Etx@}$-3>^y$}3oGMaWe!Yw^Vm5}#7`M5-SM781$ZFA)? z@7Hy&WreA2V0u{d+oxRTV?^aE-ZA#-_5kxFJLa(>sw3m7QuBGX|vn5%smH(W^ zF?d?ic!IyC5%xii5ri58(Ro9k%T4vPs&+yMJ3RJc9-*oD%-c)4WO2=8DZ--r#ec*B zz@+@#S^NZ4`X>$Nq_PP5kzmNS%sK9vNXsms&}WxussMZbh`H%%O?_|duY2Y_4cszi z%^_RYPxs1Z^}6Gky&VXE3UmMs5{vW0ZREwpdZ`a@#;w5fZ9S@v3DzqrNXd8h@#+hm zpN;pvat;{H&z5{9zlEdJmZVecqOMKGd?w9Pz1;z!9gYs;)Bi3tVQ|h@eQ1pC8S3Jr zpXhs#nNPaZ=&olM9W$B5D5&{&`h~xW0NaHfS|;fxPrHY~iZ;1O^P8oH0mNQa7oon% zuPEN9C)h0^4fV5hrVnBwjZo%@5}+Sj65C|tHG}TSKtoS`#OeUvw;vKJUKn+u96#E~ z>WM~p+`H#3hSV;Td#6LOVmuPm85BUc4tN;O884#*%1aqPYnjCMZgpGf|EG^jnaTlE z0-{x5b$d-qJ@vHf;kQ#$(CctI49TnCpZSlnby8EDq}7q@T@04<8`UV>^6)lSW7A>? z3xe&Rm+lKvs+BDmgW}leLRv;Y5}Z%ib`g?OUUNK-XlxUmu%X-;RmX#Vg_}BkYke06 z^#%R2ao@ih&NHfowy0T)V4J2Rx4 zUnegp(G}KRNfM}SmhtUL&pLdaLE@#Is>PkUh;eB#ecGC5jTpEs6YmtJLDzd|Vl9TI z{M!ovqo+VgOgji$<+4qvH`$UF{n#YSWL`kB{Ns&&9h^p&%Zc z%`Z!;cGEFZ6=N)!$d|^Ng+~7^j7V~z33juKoqM%I&pRu8nn7r-T%IwsiGlXtdGoJd z!PVFw+L_74FPqM4BU&lOZGw_(Tv(6p4V1P5)pnF~IYAtL3mh(;Eo;0$Zv+(!Xm zsSGkk=eWQrba<_CWzli$D1}h2CA?xkYsL39CQiXkt8hnfp^HfO{%4x=(CiWuQin30`hee;r1eyb-rAE9lZ8bu%Fw|OrJct!D33?3c2KDuW#HO zeNI(;iyw%5Q;xU_)rDA`uvR>JRs;D^VZ5-!J%-)W-JA?Aq7;)8T{U=BKLf4FpKE`5 zP>!oj$J*E9Bwt%Jo3`6}ldW~%iQZLBvxRp>@CP`Pwf)^*Oq1+dxPK=5nYbkTQ{h@h z>@z=^a3`w@Itv+;7HvPO9V%_9{e-kMQ2nUYSPss)AoD7eDgbFxu)KDjrcL zdVE*`T`-iFhi86lL8=P`iFmU|CfWMW$n!YW52Ld)YZe(faDDEupXku)<1%8R;V^u8 z29+<-UAqoEhz17aCOk?z-6xloy9HPn`3{?LHH7IuUFlEl8^8;{;`eEA+Vfot{+&uG zbs6Sr@z#W7JK4CqBpcIw*XSl=l*zQ$nR#YH+|TjIATqEpIz5$l2GS}2mAyw~KgO

ZG1)`LK-g{}`Nsk?hci$9H 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 Date: Fri, 20 Dec 2019 19:47:02 +0200 Subject: [PATCH 3/9] Added collapse of the register of quantum bits --- .../simulation/computation/QCollapser.java | 257 ++++++++++++++++++ .../simulation/computation/gates/QGate.java | 34 --- .../computation/qubits/QCollapser.java | 114 -------- .../simulation/computation/qubits/Qubit.java | 78 +++--- .../qubits/register/QRegister.java | 82 +++--- .../simulation/util/math/QMath.java | 64 ++++- .../com/simulationQ/util/package-info.java | 9 + .../java/com/simulation/QCollapserTest.java | 67 +++++ .../java/com/simulation/package-info.java | 9 + 9 files changed, 493 insertions(+), 221 deletions(-) create mode 100644 src/main/java/com/simulationQ/simulation/computation/QCollapser.java delete mode 100644 src/main/java/com/simulationQ/simulation/computation/qubits/QCollapser.java create mode 100644 src/main/java/com/simulationQ/util/package-info.java create mode 100644 src/test/java/com/simulation/QCollapserTest.java create mode 100644 src/test/java/com/simulation/package-info.java diff --git a/src/main/java/com/simulationQ/simulation/computation/QCollapser.java b/src/main/java/com/simulationQ/simulation/computation/QCollapser.java new file mode 100644 index 0000000..825d9f0 --- /dev/null +++ b/src/main/java/com/simulationQ/simulation/computation/QCollapser.java @@ -0,0 +1,257 @@ +/* + * 10/11/2019 17:15:52 + * QCollapser.java created by Tsvetelin + */ +package com.simulationQ.simulation.computation; + + +import java.math.BigDecimal; +import java.math.MathContext; +import java.math.RoundingMode; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import com.simulationQ.simulation.computation.qubits.Qubit; +import com.simulationQ.simulation.computation.qubits.register.CRegister; +import com.simulationQ.simulation.computation.qubits.register.QRegister; +import com.simulationQ.simulation.util.math.QMath; + + +/** + * @author Tsvetelin + * + */ +public interface QCollapser +{ + + public static final String FORMAT = "N %d -> %s"; + + /** + * Collapse a single qubit a single time, very inaccurate + * + * @param q + * - the qubit + * @return the collapsed state + */ + public static Qubit collapse ( final Qubit q ) + { + final BigDecimal onChance = q.getKetON() + .abs() + .pow( 2 ) + .setScale( QMath.PRECISION , + RoundingMode.FLOOR ); + + final BigDecimal decision = new BigDecimal( Math.random() ); + + return decision.compareTo( onChance ) == -1 ? Qubit.QUBIT_ON + : Qubit.QUBIT_OFF; + } + + /** + * Collapses the qubit multiple times, specifically n-times + * + * @param q + * - the qubit + * @param n + * - the number of times, the bigger, the better + * @return the collapsed state + */ + public static Qubit collapse ( final Qubit q , final long n ) + { + long on = 0 , off = 0; + + for ( long i = 0 ; i < n ; i++ ) + { + if ( q.collapseSuperposition().equals( Qubit.QUBIT_ON ) ) + on++; + else + off++; + } + + return on < off ? Qubit.QUBIT_OFF : Qubit.QUBIT_ON; + } + + /** + * + * collapses n times q and generates logs + * + * @param q + * - the qubit + * @param n + * - the number of collapses + * @return a list of strings for the collapses + */ + public static List< String > generateCollapseData ( final Qubit q , + final long n ) + { + final List< String > res = new LinkedList<>(); + + for ( long i = 0 ; i < n ; i++ ) + { + res.add( String.format( FORMAT , n , q.collapseSuperposition() ) ); + } + + return res; + } + + /** + * + * Generates the collapses and then applies the formatting + * + * @param q + * - the qubit + * @param n + * - the number of times + * @param format + * @return a list of logs with the specified formatting + */ + public static List< String > generateCollapseData ( final Qubit q , + final long n , + final String format ) + { + final List< String > res = new LinkedList<>(); + + for ( long i = 0 ; i < n ; i++ ) + { + res.add( String.format( format , n , q.collapseSuperposition() ) ); + } + + return res; + } + + /** + * Collapses the given register + * + */ + public static CRegister collapse ( final QRegister reg ) + { + return new CRegister( collapseToString( reg ) ); + } + + /** + * Collapses the register but returns a string representing the bits + */ + public static String collapseToString ( final QRegister reg ) + { + final BigDecimal [] coeficients = Arrays.stream( reg.getComputationalVector() + .getScalars() ) + .map( x -> x.abs() + .pow( 2 ) ) + .map( x -> x.setScale( QMath.PRECISION , + RoundingMode.FLOOR ) ) + .collect( Collectors.toUnmodifiableList() ) + .toArray( new BigDecimal[1] ); + + final BigDecimal sum = Arrays.stream( coeficients ) + .reduce( BigDecimal.ZERO , + ( res , + next ) -> res.add( next ) ); + + final List< String > values = generateBinaryStringValues( QMath.log2( coeficients.length ) ); + + final int index = indexOfRange( coeficients , QMath.RND_SUPPLY , sum ); + + return values.get( index ); + } + + /** + * + * Collapses the register n-times + * + * @param qReg + * @param rounds + * @return + */ + public static QRegister collapse ( final QRegister reg , final long rounds ) + { + final int [] counters = Arrays.stream( "0".repeat( reg.size() ) + .split( "" ) ) + .mapToInt( Integer::parseInt ) + .toArray(); + + final List< String > possibilities = generateBinaryStringValues( reg.size() ); + + for ( int i = 0 ; i < rounds ; i++ ) + { + final String collapsed = collapseToString( reg ); + + final int index = possibilities.indexOf( collapsed ); + + counters[index]++; + } + + int maxIndex = 0; + int max = counters[0]; + + for ( int i = 1 ; i < counters.length ; i++ ) + { + if ( max < counters[i] ) + { + max = counters[i]; + maxIndex = i; + } + } + + return new CRegister( possibilities.get( maxIndex ) ); + } + + /** + * + * Generates the possible combinations of 0's and 1's given the number of + * bits + * + * @param bits + * - the number of bits + * @return a list of the combinations + */ + public static List< String > generateBinaryStringValues ( final int bits ) + { + return IntStream.range( 0 , QMath.pow( 2 , bits ) ) + .mapToObj( Integer::toBinaryString ) + .map( ( String x ) -> x.length() < bits + ? "0".repeat( bits - x.length() ) + x + : x ) + .collect( Collectors.toList() ); + } + + /** + * + * NOTE: You should not be using this, if you want, do at your own risk + * + * @param coefs + * - the length of each interval + * @param rnd + * - the supplier of random numbers + * @param sumOfCoefs + * - the length of the whole interval + * @return the index of the interval in which the random number fell + * @apiNote This is for internal use ONLY, do use at your own risk + */ + public static int indexOfRange ( final BigDecimal [] coefs , + final Supplier< BigDecimal > rnd , + final BigDecimal sumOfCoefs ) + { + BigDecimal decision = rnd.get() + .multiply( sumOfCoefs , + new MathContext( QMath.PRECISION , + RoundingMode.HALF_UP ) ); + + BigDecimal currentSum = BigDecimal.ZERO; + int index = 0; + int counter = 0; + + while ( decision.compareTo( currentSum ) >= 0 ) + { + currentSum = currentSum.add( coefs[counter] ); + + index++; + counter++; + } + + return index - 1; + } +} diff --git a/src/main/java/com/simulationQ/simulation/computation/gates/QGate.java b/src/main/java/com/simulationQ/simulation/computation/gates/QGate.java index 496971b..a2381c9 100644 --- a/src/main/java/com/simulationQ/simulation/computation/gates/QGate.java +++ b/src/main/java/com/simulationQ/simulation/computation/gates/QGate.java @@ -83,11 +83,6 @@ public QRegister apply ( QRegister reg ) if ( reg.size() != this.numberInputBits ) throw new IllegalArgumentException( "The register must be with the same number of bits as the input of the gate!" ); - System.out.println( "QGate | Applying: " ); - System.out.println( "QGate | " + this.operation ); - System.out.println( "QGate | To:" ); - System.out.println( "QGate | " + reg ); - if ( this.numberInputBits == 1 ) { @@ -133,33 +128,4 @@ private static final boolean isMatrixSquare ( Matrix a ) { return a.getRows() == a.getColons(); } - -// public static void main ( String [] args ) -// { -// Matrix a = new Matrix( new ComplexNumber[][] { -// { ComplexNumber.REAL_UNIT, ComplexNumber.REAL_UNIT }, -// { ComplexNumber.REAL_UNIT, ComplexNumber.REAL_UNIT.negate() } -// } ).multiplyWithScalar( ComplexNumber.ONE_OVER_SQRT_2 ); -// -// Qubit q = Qubit.QUBIT_OFF; -// -// System.out.println( q ); -// -// System.out.println( a.square() ); -// -// QRegister reg = new QRegister( new Qubit[] { q } ); -// -// QGate g = new QGate( a , 1 , 2 , "" ) -// {}; -// -// QRegister res = g.apply( reg ); -// -// System.out.println( "Got: " + res ); -// -// System.out.println( "100 collapses: " + QCollapser.collapse( 100 , res.getQubit( 0 ) ) ); -// -// System.out.println( "Again: " + g.apply( res ) ); -// -// } - } diff --git a/src/main/java/com/simulationQ/simulation/computation/qubits/QCollapser.java b/src/main/java/com/simulationQ/simulation/computation/qubits/QCollapser.java deleted file mode 100644 index 2c6d10e..0000000 --- a/src/main/java/com/simulationQ/simulation/computation/qubits/QCollapser.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * 10/11/2019 17:15:52 - * QCollapser.java created by Tsvetelin - */ -package com.simulationQ.simulation.computation.qubits; - - -import java.math.BigDecimal; -import java.math.RoundingMode; -import java.util.LinkedList; -import java.util.List; - -import com.simulationQ.simulation.util.math.QMath; - - -/** - * @author Tsvetelin - * - */ -public interface QCollapser -{ - - public static Qubit collapse ( Qubit q ) - { - final BigDecimal onChance = q.getKetON() - .abs() - .pow( 2 ) - .setScale( QMath.PRECISION , - RoundingMode.FLOOR ); - - final BigDecimal decision = new BigDecimal( Math.random() ); - - return decision.compareTo( onChance ) == -1 ? Qubit.QUBIT_ON - : Qubit.QUBIT_OFF; - - // TODO Needed to sort out the collapsing in a better way!!!!!! - // throw new UnsupportedOperationException( "The collapse of a quibit is - // not yet supported" ); - } - - public static Qubit collapse ( long n , Qubit q ) - { - long on = 0 , off = 0; - - for ( long i = 0 ; i < n ; i++ ) - { - if ( q.collapseSuperposition().equals( Qubit.QUBIT_ON ) ) - on++; - else - off++; - } - - return on < off ? Qubit.QUBIT_OFF : Qubit.QUBIT_ON; - } - - /** - * - * collapses n times q and generates logs - * - * @param n - * @param q - * @return - */ - public static List< String > generateCollapseData ( long n , Qubit q ) - { - final List< String > res = new LinkedList<>(); - - final String format = "N %d -> %s"; - - for ( long i = 0 ; i < n ; i++ ) - { -// System.out.println( "Collapsed -> " + q.collapseSuperposition() ); -// System.out.println( String.format( format , i, q.collapseSuperposition() ) ); - res.add( String.format( format , n, q.collapseSuperposition() ) ); - } - return res; - } - -// public static void main ( String [] args ) -// { -// final String res = QCollapser.generateCollapseData( 100 , Qubit.QUBIT_HALF_HALF ) -// .toString().replaceAll( "," , System.lineSeparator() ); -// -// System.out.println( res ); -// } - - /* - * Scrapped v1.0 - * final BigDecimal onChance = q.getKetON().abs() - * .pow( 2 ) - * .setScale( QMath.PRECISION , - * RoundingMode.FLOOR ); - * - * final BigDecimal offChance = q.getKetOFF().abs() - * .pow( 2 ) - * .setScale( QMath.PRECISION , - * RoundingMode.FLOOR ); - * - * final BigDecimal distance = onChance.subtract( offChance ).abs(); - * - * final BigDecimal lower = onChance.compareTo( offChance ) ==-1 ? onChance - * : offChance; - * - * final BigDecimal decision = new BigDecimal( Math.random() ).multiply( - * distance ).subtract( lower ); - * - * final BigDecimal distanceToON = decision.subtract( onChance ).abs(); - * final BigDecimal distanceToOFF = decision.subtract( offChance ).abs(); - * return distanceToON.compareTo( distanceToOFF ) == -1 ? Qubit.QUBIT_ON : - * Qubit.QUBIT_OFF; - * ------------------------------------------------------------------------- - */ - -} diff --git a/src/main/java/com/simulationQ/simulation/computation/qubits/Qubit.java b/src/main/java/com/simulationQ/simulation/computation/qubits/Qubit.java index cec1589..d08e22a 100644 --- a/src/main/java/com/simulationQ/simulation/computation/qubits/Qubit.java +++ b/src/main/java/com/simulationQ/simulation/computation/qubits/Qubit.java @@ -7,6 +7,7 @@ import java.math.BigDecimal; +import com.simulationQ.simulation.computation.QCollapser; import com.simulationQ.simulation.util.math.QMath; import com.simulationQ.simulation.util.math.QMath.Constants; import com.simulationQ.simulation.util.math.complexNumbers.ComplexNumber; @@ -66,11 +67,6 @@ public ComplexNumber getKetON () return ketON; } - public Qubit collapseSuperposition () - { - return QCollapser.collapse( this ); - } - public Vector getAsVector () { return new Vector( new ComplexNumber[] { ketOFF, ketON } ); @@ -93,41 +89,41 @@ public String toString () this.ketON.toString() ); } - public static void main ( String [] args ) - { - System.out.println( "Testing..." ); - - final int iterations = 1000000; - - final ComplexNumber oneSqrt2 = new ComplexNumber( Constants.ONE_OVER_SQRT_2.value , - BigDecimal.ZERO ); - - System.out.println( oneSqrt2.abs().pow( 2 ) ); - - Qubit q = new Qubit( oneSqrt2 , oneSqrt2 ); - - int ones = 0; - int zeroes = 0; - - for ( int i = 0 ; i < iterations ; i++ ) - { - - boolean isOn = q.collapseSuperposition().equals( QUBIT_ON ); - - if ( isOn ) - { - ones++; - } else - { - zeroes++; - } - } - - System.out.println( "Final results: " ); - System.out.println( "On states: " + ones ); - System.out.println( "Off state: " + zeroes ); - System.out.println( "Total: " + ( ones + zeroes ) ); - - } +// public static void main ( String [] args ) +// { +// System.out.println( "Testing..." ); +// +// final int iterations = 1000000; +// +// final ComplexNumber oneSqrt2 = new ComplexNumber( Constants.ONE_OVER_SQRT_2.value , +// BigDecimal.ZERO ); +// +// System.out.println( oneSqrt2.abs().pow( 2 ) ); +// +// Qubit q = new Qubit( oneSqrt2 , oneSqrt2 ); +// +// int ones = 0; +// int zeroes = 0; +// +// for ( int i = 0 ; i < iterations ; i++ ) +// { +// +// boolean isOn = q.collapseSuperposition().equals( QUBIT_ON ); +// +// if ( isOn ) +// { +// ones++; +// } else +// { +// zeroes++; +// } +// } +// +// System.out.println( "Final results: " ); +// System.out.println( "On states: " + ones ); +// System.out.println( "Off state: " + zeroes ); +// System.out.println( "Total: " + ( ones + zeroes ) ); +// +// } } diff --git a/src/main/java/com/simulationQ/simulation/computation/qubits/register/QRegister.java b/src/main/java/com/simulationQ/simulation/computation/qubits/register/QRegister.java index c90821b..b6db76b 100644 --- a/src/main/java/com/simulationQ/simulation/computation/qubits/register/QRegister.java +++ b/src/main/java/com/simulationQ/simulation/computation/qubits/register/QRegister.java @@ -5,11 +5,8 @@ package com.simulationQ.simulation.computation.qubits.register; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - import com.simulationQ.simulation.computation.qubits.Qubit; +import com.simulationQ.simulation.util.math.QMath; import com.simulationQ.simulation.util.math.matrices.vectors.Vector; @@ -20,53 +17,76 @@ public class QRegister { - private final List< Qubit > register = new ArrayList<>(); + private final Vector computationalVector; public QRegister ( Qubit [] qubits ) { - Arrays.stream( qubits ).forEach( this.register::add ); + this.computationalVector = getAsComputationalVector( qubits ); + } + + /** + * + * This should be used with caution as it does not perform checking as the vector might be entangled + * + * @param vector + */ + public QRegister ( Vector vector ) + { + this.computationalVector = vector; + } + + public Qubit head () + { + return this.computationalVector.size() == 2 + ? new Qubit( this.computationalVector.getAt( 0 ) , + this.computationalVector.getAt( 1 ) ) + : null; + } + + public final int size () + { + return QMath.log2( this.computationalVector.size() ); } + + /** + * @return the computationalVector + */ public Vector getComputationalVector () { - Vector res = this.register.get( 0 ).getAsVector(); - - for( int i=1;i RND_SUPPLY = () -> new BigDecimal( new Random().nextDouble() ); /** * Some constants for calculation @@ -91,7 +95,65 @@ public static boolean checkLinearCombinationEqualToOne ( final BigDecimal a , return a.pow( 2 ) .add( b.pow( 2 ) ) .sqrt( new MathContext( PRECISION ) ) - .setScale( BigDecimal.ONE.scale(), RoundingMode.HALF_UP ) + .setScale( BigDecimal.ONE.scale() , RoundingMode.HALF_UP ) .equals( BigDecimal.ONE ); } + + /** + * + * The log2 of the supplied number + * + * @apiNote as it returns int the supplied size should be a power of + * 2 + * + * @param num + * @return log2(size) + */ + public static int log2 ( int num ) + { + switch ( num ) + { + case 1 : + return 0; + case 2 : + return 1; + case 4 : + return 2; + case 8 : + return 3; + case 16 : + return 4; + case 32 : + return 5; + case 64 : + return 6; + case 128 : + return 7; + case 256 : + return 8; + case 512 : + return 9; + case 1024 : + return 10; + default : + throw new IllegalArgumentException( "This method works only with powers of 2" ); + } + + } + + /** + * @param base + * @param pow + * @return + */ + public static int pow ( final int base , final int pow ) + { + if( pow < 0 ) throw new ArithmeticException( "The power must be greater than 0" ); + + int res = 1; + + for( int i=0;i Date: Fri, 20 Dec 2019 19:47:59 +0200 Subject: [PATCH 4/9] Added simple separation of threads and between the engine, simulation and GUI --- src/main/java/com/simulationQ/Engine.java | 49 +++ src/main/java/com/simulationQ/Mediator.java | 46 +++ .../java/com/simulationQ/SimulationRound.java | 61 +++ .../java/com/simulationQ/gui/MainWindow.java | 361 ------------------ .../computation/QFinalStateCalculator.java | 31 ++ .../qubits/register/CRegister.java | 70 ++++ .../simulationQ/util/async/ASyncWaiter.java | 21 + .../simulationQ/util/async/package-info.java | 9 + 8 files changed, 287 insertions(+), 361 deletions(-) create mode 100644 src/main/java/com/simulationQ/Engine.java create mode 100644 src/main/java/com/simulationQ/Mediator.java create mode 100644 src/main/java/com/simulationQ/SimulationRound.java delete mode 100644 src/main/java/com/simulationQ/gui/MainWindow.java create mode 100644 src/main/java/com/simulationQ/simulation/computation/QFinalStateCalculator.java create mode 100644 src/main/java/com/simulationQ/simulation/computation/qubits/register/CRegister.java create mode 100644 src/main/java/com/simulationQ/util/async/ASyncWaiter.java create mode 100644 src/main/java/com/simulationQ/util/async/package-info.java diff --git a/src/main/java/com/simulationQ/Engine.java b/src/main/java/com/simulationQ/Engine.java new file mode 100644 index 0000000..4e6abd6 --- /dev/null +++ b/src/main/java/com/simulationQ/Engine.java @@ -0,0 +1,49 @@ +/* + * 06/12/2019 09:45:06 + * Engine.java created by Tsvetelin + */ +package com.simulationQ; + + +import java.io.Closeable; +import java.io.IOException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +import com.simulationQ.simulation.computation.qubits.register.QRegister; + + +/** + * @author Tsvetelin + * + */ +public class Engine implements Closeable +{ + + public static final Engine instance = new Engine(); + + private static final int NEEDED_THREADS = 2; + + private ExecutorService ex; + + /** + * + */ + private Engine () + { + this.ex = Executors.newFixedThreadPool( NEEDED_THREADS ); + } + + public Future< QRegister > runSimulation ( SimulationRound round ) + { + return ex.submit( round::runSimulation ); + } + + @Override + public void close () throws IOException + { + ex.shutdown(); + } + +} diff --git a/src/main/java/com/simulationQ/Mediator.java b/src/main/java/com/simulationQ/Mediator.java new file mode 100644 index 0000000..f0c3f28 --- /dev/null +++ b/src/main/java/com/simulationQ/Mediator.java @@ -0,0 +1,46 @@ +/* + * 08/12/2019 13:57:36 + * Mediator.java created by Tsvetelin + */ +package com.simulationQ; + + +import java.util.List; +import java.util.concurrent.Future; + +import com.simulationQ.simulation.computation.QFinalStateCalculator; +import com.simulationQ.simulation.computation.gates.QGate; +import com.simulationQ.simulation.computation.qubits.register.QRegister; + + +/** + * + * This is the mediator between the GUI and the engine + * + * @author Tsvetelin + * + */ +public final class Mediator +{ + + public static final Mediator instance = new Mediator(); + + /** + * + */ + private Mediator () + {} + + public final Future< QRegister > runSimulation ( final List< QGate > gates , + final QRegister startState , + final long rounds ) + { + final QRegister finalState = QFinalStateCalculator.calculateFinalState( gates , + startState ); + final SimulationRound round = new SimulationRound( finalState , + rounds ); + return Engine.instance.runSimulation( round ); + } + + +} diff --git a/src/main/java/com/simulationQ/SimulationRound.java b/src/main/java/com/simulationQ/SimulationRound.java new file mode 100644 index 0000000..61411b0 --- /dev/null +++ b/src/main/java/com/simulationQ/SimulationRound.java @@ -0,0 +1,61 @@ +/* + * 06/12/2019 09:47:23 + * SimulationRound.java created by Tsvetelin + */ +package com.simulationQ; + + +import com.simulationQ.simulation.computation.QCollapser; +import com.simulationQ.simulation.computation.qubits.register.QRegister; + + +/** + * @author Tsvetelin + * + */ +public class SimulationRound implements Runnable +{ + + private final QRegister qReg; + + private final long rounds; + + private QRegister results; + + /** + * Constructs a object representing the finished state of the computation, + * + * @param qReg + * - the register to be collapsed + * @param rounds + * - how many times to collapse + */ + public SimulationRound ( final QRegister qReg , final long rounds ) + { + this.qReg = qReg; + this.rounds = rounds; + } + + @Override + public void run () + { + this.results = runSimulation(); + } + + /** + * @return the state after collapse + */ + public QRegister runSimulation () + { + return QCollapser.collapse( this.qReg , this.rounds ); + } + + /** + * @return the results + */ + public QRegister getResults () + { + return this.results; + } + +} diff --git a/src/main/java/com/simulationQ/gui/MainWindow.java b/src/main/java/com/simulationQ/gui/MainWindow.java deleted file mode 100644 index bf7c3f1..0000000 --- a/src/main/java/com/simulationQ/gui/MainWindow.java +++ /dev/null @@ -1,361 +0,0 @@ -/* - * 27/11/2019 16:07:58 - * MainWindow.java created by Tsvetelin - */ -package com.simulationQ.GUI; - - -import java.awt.Toolkit; -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; - -import javax.swing.JButton; -import javax.swing.JComboBox; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JTextArea; -import javax.swing.border.EmptyBorder; - -import com.simulationQ.simulation.computation.gates.QGate; -import com.simulationQ.simulation.computation.gates.impl.Hadamard; -import com.simulationQ.simulation.computation.gates.impl.NOT; -import com.simulationQ.simulation.computation.gates.impl.PauliY; -import com.simulationQ.simulation.computation.gates.impl.PauliZ; -import com.simulationQ.simulation.computation.qubits.QCollapser; -import com.simulationQ.simulation.computation.qubits.Qubit; -import com.simulationQ.simulation.computation.qubits.register.QRegister; - - -/** - * @author Tsvetelin - * - */ -@Deprecated -public class MainWindow extends JFrame implements Runnable -{ - - /** - * - */ - private static final long serialVersionUID = 1L; - - private JPanel contentPane; - - private JLabel lblRes; - - private JTextArea txtCollapseCounting; - - private JComboBox< String > gate1; - - private JComboBox< String > gate2; - - private JComboBox< String > gate3; - - private QGate hadamard = new Hadamard(); - - private QGate not = new NOT(); - - private QGate pauliY = new PauliY(); - - private QGate pauliZ = new PauliZ(); - - public void run () - { - try - { - MainWindow frame = new MainWindow(); - frame.setVisible( true ); - } catch ( Exception e ) - { - e.printStackTrace(); - } - } - - /** - * Create the frame. - */ - public MainWindow () - { - setTitle( "Simulation Q" ); - setIconImage( Toolkit.getDefaultToolkit() - .getImage( MainWindow.class.getResource( "/com/simulationQ/gui/icon/logo.png" ) ) ); - setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); - setBounds( 100 , 100 , 800 , 800 ); - - this.setLocationRelativeTo( null ); - - contentPane = new JPanel(); - contentPane.setBorder( new EmptyBorder( 5 , 5 , 5 , 5 ) ); - setContentPane( contentPane ); - contentPane.setLayout( null ); - - JLabel lblClarificationCollapseCounting = new JLabel( "Number of Collapses" ); - lblClarificationCollapseCounting.setBounds( 10 , 10 , 200 , 20 ); - contentPane.add( lblClarificationCollapseCounting ); - - txtCollapseCounting = new JTextArea( "100" ); - txtCollapseCounting.setBounds( 150 , 10 , 200 , 20 ); - contentPane.add( txtCollapseCounting ); - - JLabel lblQubit = new JLabel( "|1>" ); - lblQubit.setBounds( 10 , 40 , 20 , 20 ); - contentPane.add( lblQubit ); - - JButton btnCollapse = new JButton( "Collapse" ); - btnCollapse.setBounds( 10 , 700 , 750 , 30 ); - - btnCollapse.addMouseListener( new MouseListener() - { - - public void mouseReleased ( MouseEvent e ) - {} - - public void mousePressed ( MouseEvent e ) - {} - - public void mouseExited ( MouseEvent e ) - {} - - public void mouseEntered ( MouseEvent e ) - {} - - public void mouseClicked ( MouseEvent e ) - { - updateCollapseInfo(); - } - } ); - - contentPane.add( btnCollapse ); - - lblRes = new JLabel( Qubit.QUBIT_ON.toString() ); - lblRes.setBounds( 440 , 40 , 500 , 20 ); - lblRes.setVisible( false ); - contentPane.add( lblRes ); - - JButton btnCollapseWithoutFinalResult = new JButton( "Show computation value" ); - btnCollapseWithoutFinalResult.setBounds( 10 , 657 , 750 , 30 ); - - btnCollapseWithoutFinalResult.addMouseListener( new MouseListener() - { - - public void mouseReleased ( MouseEvent e ) - {} - - public void mousePressed ( MouseEvent e ) - {} - - public void mouseExited ( MouseEvent e ) - {} - - public void mouseEntered ( MouseEvent e ) - {} - - public void mouseClicked ( MouseEvent e ) - { - updateIntospectionInfo(); - } - } ); - - contentPane.add( btnCollapseWithoutFinalResult ); - - gate1 = new JComboBox< String >(); - gate1.setBounds( 70 , 40 , 100 , 20 ); - - gate1.addItem( "None" ); - gate1.addItem( "Hadamard" ); - gate1.addItem( "Pauli-X/NOT" ); - gate1.addItem( "Pauli-Y" ); - gate1.addItem( "Pauli-Z" ); - - contentPane.add( gate1 ); - - gate2 = new JComboBox< String >(); - gate2.setBounds( 190 , 40 , 100 , 20 ); - - gate2.addItem( "None" ); - gate2.addItem( "Hadamard" ); - gate2.addItem( "Pauli-X/NOT" ); - gate2.addItem( "Pauli-Y" ); - gate2.addItem( "Pauli-Z" ); - - contentPane.add( gate2 ); - - gate3 = new JComboBox< String >(); - gate3.setBounds( 310 , 40 , 100 , 20 ); - - gate3.addItem( "None" ); - gate3.addItem( "Hadamard" ); - gate3.addItem( "Pauli-X/NOT" ); - gate3.addItem( "Pauli-Y" ); - gate3.addItem( "Pauli-Z" ); - - contentPane.add( gate3 ); - } - -// public void paint ( Graphics gp ) -// { -// super.paint( gp ); -// Graphics2D graphics = ( Graphics2D ) gp; -// // Line2D line = new Line2D.Float( 40 , 80 , 300 , 80 ); -// // graphics.draw( line ); -// } - - private void updateCollapseInfo () - { - lblRes.setVisible( true ); - - Qubit input = Qubit.QUBIT_ON; - QRegister inputReg = new QRegister( new Qubit[] { input } ); - - switch ( gate1.getSelectedIndex() ) - { - case 1 : - - input = hadamard.apply( inputReg ).getQubit( 0 ); - break; - case 2 : - input = not.apply( inputReg ).getQubit( 0 ); - break; - case 3 : - input = pauliY.apply( inputReg ).getQubit( 0 ); - break; - case 4 : - input = pauliZ.apply( inputReg ).getQubit( 0 ); - break; - - default : - break; - } - - inputReg = new QRegister( new Qubit[] { input } ); - - switch ( gate2.getSelectedIndex() ) - { - case 1 : - - input = hadamard.apply( inputReg ).getQubit( 0 ); - break; - case 2 : - input = not.apply( inputReg ).getQubit( 0 ); - break; - case 3 : - input = pauliY.apply( inputReg ).getQubit( 0 ); - break; - case 4 : - input = pauliZ.apply( inputReg ).getQubit( 0 ); - break; - - default : - break; - } - - inputReg = new QRegister( new Qubit[] { input } ); - - switch ( gate3.getSelectedIndex() ) - { - case 1 : - input = hadamard.apply( inputReg ).getQubit( 0 ); - break; - case 2 : - input = not.apply( inputReg ).getQubit( 0 ); - break; - case 3 : - input = pauliY.apply( inputReg ).getQubit( 0 ); - break; - case 4 : - input = pauliZ.apply( inputReg ).getQubit( 0 ); - break; - - default : - break; - } -// -// inputReg = new QRegister( new Qubit[] { input } ); - - Qubit res = QCollapser.collapse( Long.parseLong( txtCollapseCounting.getText() ) , - input ); - - lblRes.setText( res.toString() ); - - this.paint( this.getGraphics() ); - - } - - private void updateIntospectionInfo () - { - lblRes.setVisible( true ); - - Qubit input = Qubit.QUBIT_ON; - - QRegister inputReg = new QRegister( new Qubit[] { input } ); - - switch ( gate1.getSelectedIndex() ) - { - case 1 : - - input = hadamard.apply( inputReg ).getQubit( 0 ); - break; - case 2 : - input = not.apply( inputReg ).getQubit( 0 ); - break; - case 3 : - input = pauliY.apply( inputReg ).getQubit( 0 ); - break; - case 4 : - input = pauliZ.apply( inputReg ).getQubit( 0 ); - break; - - default : - break; - } - - inputReg = new QRegister( new Qubit[] { input } ); - - switch ( gate2.getSelectedIndex() ) - { - case 1 : - - input = hadamard.apply( inputReg ).getQubit( 0 ); - break; - case 2 : - input = not.apply( inputReg ).getQubit( 0 ); - break; - case 3 : - input = pauliY.apply( inputReg ).getQubit( 0 ); - break; - case 4 : - input = pauliZ.apply( inputReg ).getQubit( 0 ); - break; - - default : - break; - } - inputReg = new QRegister( new Qubit[] { input } ); - - switch ( gate3.getSelectedIndex() ) - { - case 1 : - input = hadamard.apply( inputReg ).getQubit( 0 ); - break; - case 2 : - input = not.apply( inputReg ).getQubit( 0 ); - break; - case 3 : - input = pauliY.apply( inputReg ).getQubit( 0 ); - break; - case 4 : - input = pauliZ.apply( inputReg ).getQubit( 0 ); - break; - - default : - break; - } - - lblRes.setText( input.toString() ); - - - this.paint( this.getGraphics() ); - - } -} diff --git a/src/main/java/com/simulationQ/simulation/computation/QFinalStateCalculator.java b/src/main/java/com/simulationQ/simulation/computation/QFinalStateCalculator.java new file mode 100644 index 0000000..1dfb355 --- /dev/null +++ b/src/main/java/com/simulationQ/simulation/computation/QFinalStateCalculator.java @@ -0,0 +1,31 @@ +/* + * 08/12/2019 14:03:39 + * QFinalStateCalculator.java created by Tsvetelin + */ +package com.simulationQ.simulation.computation; + + +import java.util.List; + +import com.simulationQ.simulation.computation.gates.QGate; +import com.simulationQ.simulation.computation.qubits.register.QRegister; + + +/** + * @author Tsvetelin + * + */ +public interface QFinalStateCalculator +{ + + public static QRegister calculateFinalState ( List< QGate > gates , + QRegister startState ) + { + + return gates.stream() + .reduce( startState , + ( state , gate ) -> gate.apply( state ) , + ( oldState , newState ) -> newState ); + } + +} diff --git a/src/main/java/com/simulationQ/simulation/computation/qubits/register/CRegister.java b/src/main/java/com/simulationQ/simulation/computation/qubits/register/CRegister.java new file mode 100644 index 0000000..921db82 --- /dev/null +++ b/src/main/java/com/simulationQ/simulation/computation/qubits/register/CRegister.java @@ -0,0 +1,70 @@ +/* + * 20/12/2019 10:59:26 + * CRegister.java created by Tsvetelin + */ +package com.simulationQ.simulation.computation.qubits.register; + + +import com.simulationQ.simulation.computation.qubits.Qubit; +import com.simulationQ.simulation.util.math.complexNumbers.ComplexNumber; + + +/** + * @author Tsvetelin + * + */ +public class CRegister extends QRegister +{ + + private final String state; + + public CRegister ( final String bits ) + { + super( convertToVector( bits ) ); + + this.state = bits; + + } + + /** + * @param bits + * @return + */ + private static Qubit [] convertToVector ( String bits ) + { + Qubit [] qubits = new Qubit[bits.length()]; + + for ( int i = 0 ; i < bits.length() ; i++ ) + { + qubits[i] = bits.charAt( i ) == '1' ? Qubit.QUBIT_ON + : Qubit.QUBIT_OFF; + } + return qubits; + } + + public int [] getState () + { + final int [] res = new int[this.size()]; + + for ( int i = 0 ; i < res.length ; i++ ) + { + res[i] = state.charAt( i ) == '1' ? 1 : 0; + } + + return res; + } + + @Override + public String toString () + { + StringBuilder sb = new StringBuilder(); + + for ( ComplexNumber bit : this.getComputationalVector() ) + { + sb.append( bit.equals( ComplexNumber.REAL_UNIT ) ? '1' : '0' ); + } + + return sb.toString(); + } + +} diff --git a/src/main/java/com/simulationQ/util/async/ASyncWaiter.java b/src/main/java/com/simulationQ/util/async/ASyncWaiter.java new file mode 100644 index 0000000..2d1cbc9 --- /dev/null +++ b/src/main/java/com/simulationQ/util/async/ASyncWaiter.java @@ -0,0 +1,21 @@ +/* + * 08/12/2019 16:02:51 + * ASyncWaiter.java created by Tsvetelin + */ +package com.simulationQ.util.async; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +/** + * @author Tsvetelin + * + */ +public interface ASyncWaiter +{ + public static T awaitRes( final Future< T > f ) throws InterruptedException, ExecutionException + { + while(!f.isDone()); + return f.get(); + } +} diff --git a/src/main/java/com/simulationQ/util/async/package-info.java b/src/main/java/com/simulationQ/util/async/package-info.java new file mode 100644 index 0000000..bf8b096 --- /dev/null +++ b/src/main/java/com/simulationQ/util/async/package-info.java @@ -0,0 +1,9 @@ +/* + * 08/12/2019 16:02:40 + * package-info.java created by Tsvetelin + */ +/** + * @author Tsvetelin + * + */ +package com.simulationQ.util.async; From 7cd6300d1b2864462ae192f3b17825798fd046fa Mon Sep 17 00:00:00 2001 From: Tsvetelin Kostadinov Date: Sun, 22 Dec 2019 16:16:09 +0200 Subject: [PATCH 5/9] The errors didn't show up last time --- .../com/simulationQ/simulation/computation/QCollapser.java | 6 +++--- .../simulationQ/simulation/computation/qubits/Qubit.java | 4 ---- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/simulationQ/simulation/computation/QCollapser.java b/src/main/java/com/simulationQ/simulation/computation/QCollapser.java index 825d9f0..c2a4632 100644 --- a/src/main/java/com/simulationQ/simulation/computation/QCollapser.java +++ b/src/main/java/com/simulationQ/simulation/computation/QCollapser.java @@ -66,7 +66,7 @@ public static Qubit collapse ( final Qubit q , final long n ) for ( long i = 0 ; i < n ; i++ ) { - if ( q.collapseSuperposition().equals( Qubit.QUBIT_ON ) ) + if ( collapse( q ).equals( Qubit.QUBIT_ON ) ) on++; else off++; @@ -92,7 +92,7 @@ public static List< String > generateCollapseData ( final Qubit q , for ( long i = 0 ; i < n ; i++ ) { - res.add( String.format( FORMAT , n , q.collapseSuperposition() ) ); + res.add( String.format( FORMAT , n , collapse( q ) ) ); } return res; @@ -117,7 +117,7 @@ public static List< String > generateCollapseData ( final Qubit q , for ( long i = 0 ; i < n ; i++ ) { - res.add( String.format( format , n , q.collapseSuperposition() ) ); + res.add( String.format( format , n , collapse( q ) ) ); } return res; diff --git a/src/main/java/com/simulationQ/simulation/computation/qubits/Qubit.java b/src/main/java/com/simulationQ/simulation/computation/qubits/Qubit.java index d08e22a..5cb9702 100644 --- a/src/main/java/com/simulationQ/simulation/computation/qubits/Qubit.java +++ b/src/main/java/com/simulationQ/simulation/computation/qubits/Qubit.java @@ -5,11 +5,7 @@ package com.simulationQ.simulation.computation.qubits; -import java.math.BigDecimal; - -import com.simulationQ.simulation.computation.QCollapser; import com.simulationQ.simulation.util.math.QMath; -import com.simulationQ.simulation.util.math.QMath.Constants; import com.simulationQ.simulation.util.math.complexNumbers.ComplexNumber; import com.simulationQ.simulation.util.math.matrices.vectors.Vector; From 4e34dd3a01826a38e7fb3965d80691a319577e28 Mon Sep 17 00:00:00 2001 From: Tsvetelin Kostadinov Date: Wed, 25 Dec 2019 16:07:55 +0200 Subject: [PATCH 6/9] Fixed some math mistakes I have made, also added a method for calculating the Kronecker product of 2 matrices --- src/main/java/com/simulationQ/Main.java | 32 ++++- .../simulation/computation/QCollapser.java | 4 +- .../math/complexNumbers/ComplexNumber.java | 8 +- .../util/math/complexNumbers/RealNumber.java | 38 ++++++ .../simulation/util/math/matrices/Matrix.java | 4 +- .../util/math/matrices/MatrixOperations.java | 109 ++++++++++++------ .../util/math/matrices/vectors/Vector.java | 4 +- .../EngineTryingOfConcurrencyTest.java | 57 +++++++++ 8 files changed, 211 insertions(+), 45 deletions(-) create mode 100644 src/main/java/com/simulationQ/simulation/util/math/complexNumbers/RealNumber.java create mode 100644 src/test/java/com/simulation/EngineTryingOfConcurrencyTest.java diff --git a/src/main/java/com/simulationQ/Main.java b/src/main/java/com/simulationQ/Main.java index 510507c..2c23364 100644 --- a/src/main/java/com/simulationQ/Main.java +++ b/src/main/java/com/simulationQ/Main.java @@ -4,6 +4,7 @@ */ package com.simulationQ; + /** * @author Tsvetelin * @@ -16,13 +17,42 @@ public class Main */ public Main () {} - + /** + * * @param args */ public static void main ( String [] args ) { + try + { + switch( args[0] ) + { + case "gui": startInGUIMode(args); break; + case "nogui": startInNOGUIMode(args); break; + default: System.out.println( "Unexpected command: " + args[0] ); + } + } catch ( IndexOutOfBoundsException e ) + { + System.out.println( "You must specify the mode first - gui/nogui" ); + } + } + + /** + * @param args + */ + private static void startInGUIMode ( String [] args ) + { + System.out.println( "String GUI" ); } + /** + * @param args + */ + private static void startInNOGUIMode ( String [] args ) + { + System.out.println( "String NOGUI" ); + } + } diff --git a/src/main/java/com/simulationQ/simulation/computation/QCollapser.java b/src/main/java/com/simulationQ/simulation/computation/QCollapser.java index c2a4632..ce6229a 100644 --- a/src/main/java/com/simulationQ/simulation/computation/QCollapser.java +++ b/src/main/java/com/simulationQ/simulation/computation/QCollapser.java @@ -168,13 +168,13 @@ public static String collapseToString ( final QRegister reg ) */ public static QRegister collapse ( final QRegister reg , final long rounds ) { - final int [] counters = Arrays.stream( "0".repeat( reg.size() ) + final int [] counters = Arrays.stream( "0".repeat( QMath.pow( 2 , reg.size() ) ) .split( "" ) ) .mapToInt( Integer::parseInt ) .toArray(); final List< String > possibilities = generateBinaryStringValues( reg.size() ); - + for ( int i = 0 ; i < rounds ; i++ ) { final String collapsed = collapseToString( reg ); diff --git a/src/main/java/com/simulationQ/simulation/util/math/complexNumbers/ComplexNumber.java b/src/main/java/com/simulationQ/simulation/util/math/complexNumbers/ComplexNumber.java index c68c861..18815c0 100644 --- a/src/main/java/com/simulationQ/simulation/util/math/complexNumbers/ComplexNumber.java +++ b/src/main/java/com/simulationQ/simulation/util/math/complexNumbers/ComplexNumber.java @@ -19,7 +19,7 @@ * @author Tsvetelin * */ -public final class ComplexNumber +public class ComplexNumber { /** @@ -138,17 +138,17 @@ public ComplexNumber ( String s ) public static ComplexNumber real ( BigDecimal real ) { - return new ComplexNumber( real , BigDecimal.ZERO ); + return new RealNumber( real ); } public static ComplexNumber real ( double real ) { - return new ComplexNumber( new BigDecimal( real ) , BigDecimal.ZERO ); + return new RealNumber( new BigDecimal( real ) ); } public static ComplexNumber real ( String real ) { - return new ComplexNumber( new BigDecimal( real ) , BigDecimal.ZERO ); + return new RealNumber( new BigDecimal( real ) ); } public static ComplexNumber imaginary ( BigDecimal imaginary ) diff --git a/src/main/java/com/simulationQ/simulation/util/math/complexNumbers/RealNumber.java b/src/main/java/com/simulationQ/simulation/util/math/complexNumbers/RealNumber.java new file mode 100644 index 0000000..8a8b0f8 --- /dev/null +++ b/src/main/java/com/simulationQ/simulation/util/math/complexNumbers/RealNumber.java @@ -0,0 +1,38 @@ +/* + * 25/12/2019 15:54:55 + * RealNumber.java created by Tsvetelin + */ +package com.simulationQ.simulation.util.math.complexNumbers; + +import java.math.BigDecimal; + +/** + * @author Tsvetelin + * + */ +public class RealNumber extends ComplexNumber +{ + + /** + * + */ + public RealNumber (BigDecimal value) + { + super( value , BigDecimal.ZERO ); + } + + /** + * + */ + public RealNumber (String value) + { + super( value , "0" ); + } + + @Override + public String toString () + { + return this.getReal().toPlainString(); + } + +} diff --git a/src/main/java/com/simulationQ/simulation/util/math/matrices/Matrix.java b/src/main/java/com/simulationQ/simulation/util/math/matrices/Matrix.java index 2729b0c..6f673f6 100644 --- a/src/main/java/com/simulationQ/simulation/util/math/matrices/Matrix.java +++ b/src/main/java/com/simulationQ/simulation/util/math/matrices/Matrix.java @@ -101,7 +101,7 @@ public Matrix ( final int rows , final int colons ) * @param colons * @return the multiplicative identity. */ - public static final ComplexNumber [] [] multiplicativeIdentity ( final int rows , + public static final Matrix multiplicativeIdentity ( final int rows , final int colons ) { final ComplexNumber [] [] res = new ComplexNumber[rows][colons]; @@ -120,7 +120,7 @@ public Matrix ( final int rows , final int colons ) } } - return res; + return new Matrix( res ); } diff --git a/src/main/java/com/simulationQ/simulation/util/math/matrices/MatrixOperations.java b/src/main/java/com/simulationQ/simulation/util/math/matrices/MatrixOperations.java index 73c25a4..0f146b8 100644 --- a/src/main/java/com/simulationQ/simulation/util/math/matrices/MatrixOperations.java +++ b/src/main/java/com/simulationQ/simulation/util/math/matrices/MatrixOperations.java @@ -4,11 +4,13 @@ */ package com.simulationQ.simulation.util.math.matrices; + import com.simulationQ.simulation.util.math.ArithmeticOperations; import com.simulationQ.simulation.util.math.complexNumbers.ComplexNumber; import com.simulationQ.simulation.util.math.functional.TriFunction; import com.simulationQ.simulation.util.math.matrices.vectors.Vector; + /** * * An extension to arithmetic operations for matrices @@ -42,17 +44,23 @@ public static Matrix multiplyWithScalar ( final ComplexNumber a , { return b.multiplyWithScalar( a ); } - + /** * Applies the trifunction to each element of the matrix - * @param mapper - a trifunction receiving the x, y and value and producing the new value + * + * @param mapper + * - a trifunction receiving the x, y and value and producing the + * new value * @return */ public Matrix map ( final TriFunction< Integer , Integer , ComplexNumber , ComplexNumber > mapper ); /** * Applies the trifunction to each element of the matrix - * @param mapper - a trifunction receiving the x, y and value and producing the new value + * + * @param mapper + * - a trifunction receiving the x, y and value and producing the + * new value * @return */ public static Matrix map ( final TriFunction< Integer , Integer , ComplexNumber , ComplexNumber > mapper , @@ -137,36 +145,69 @@ public static Vector multiply ( final Matrix a , final Vector b ) } - // public static void main ( String [] args ) - // { - // Matrix m = new Matrix( new ComplexNumber[][] { - // { ComplexNumber.REAL_UNIT, ComplexNumber.REAL_UNIT.negate(), - // ComplexNumber.REAL_UNIT.add( ComplexNumber.REAL_UNIT ) }, // 1, - // // -1, - // // 2 - // { ComplexNumber.ORIGIN, - // ComplexNumber.REAL_UNIT.add( ComplexNumber.REAL_UNIT.add( - // ComplexNumber.REAL_UNIT ) ) - // .negate(), - // ComplexNumber.REAL_UNIT } // 0, -3, 1 - // } ); - // - // System.out.println( m ); - // - // Vector v = new Vector( new ComplexNumber[] { - // ComplexNumber.REAL_UNIT.add( ComplexNumber.REAL_UNIT ), - // ComplexNumber.REAL_UNIT, - // ComplexNumber.ORIGIN - // } ); - // - // System.out.println( ); - // - // System.out.println( v ); - // - // System.out.println( ); - // - // System.out.println( MatrixOperations.multiply( m , v ) ); - // - // } + /** + * Find the Kronecker product of the arguments. + * + * @param a + * The first matrix to multiply. + * @param b + * The second matrix to multiply. + * @return A new matrix: the Kronecker product of the arguments. + * + * @author https://rosettacode.org/wiki/Kronecker_product#Java + */ + public static Matrix productKronecker ( final Matrix a , final Matrix b ) + { + // Create matrix c as the matrix to fill and return. + // The length of a matrix is its number of rows. + final ComplexNumber [] [] res = new ComplexNumber[a.getRows() + * b.getRows()][]; + + // Fill in the (empty) rows of c. + // The length of each row is the number of columns. + for ( int ix = 0 ; ix < res.length ; ix++ ) + { + final int colomns = a.getColons() * b.getColons(); + res[ix] = new ComplexNumber[colomns]; + } + + // Now fill in the values: the products of each pair. + // Go through all the elements of a. + for ( int ia = 0 ; ia < a.getRows() ; ia++ ) + { + for ( int ja = 0 ; ja < a.getColons() ; ja++ ) + { + // For each element of a, multiply it by all the elements of b. + for ( int ib = 0 ; ib < b.getRows() ; ib++ ) + { + for ( int jb = 0 ; jb < b.getColons() ; jb++ ) + { + res[b.getRows() * ia + ib][b.getColons() * ja + + jb] = a.getAt( ia , ja ) + .multiply( b.getAt( ib , jb ) ); + } + } + } + } + + // Return the completed product matrix c. + return new Matrix( res ); + } + +// public static void main ( String [] args ) +// { +// final Matrix m1 = new Matrix( new ComplexNumber[][] { +// { ComplexNumber.real( "1" ), ComplexNumber.real( "2" ) }, +// { ComplexNumber.real( "3" ), ComplexNumber.real( "4" ) } +// } ); +// +// final Matrix m2 = new Matrix( new ComplexNumber[][] { +// { ComplexNumber.real( "0" ), ComplexNumber.real( "5" ) }, +// { ComplexNumber.real( "6" ), ComplexNumber.real( "7" ) } +// } ); +// +// System.out.println( productKronecker( m1 , m2 ) ); +// +// } } diff --git a/src/main/java/com/simulationQ/simulation/util/math/matrices/vectors/Vector.java b/src/main/java/com/simulationQ/simulation/util/math/matrices/vectors/Vector.java index e7193c9..5dd24c3 100644 --- a/src/main/java/com/simulationQ/simulation/util/math/matrices/vectors/Vector.java +++ b/src/main/java/com/simulationQ/simulation/util/math/matrices/vectors/Vector.java @@ -100,10 +100,10 @@ public Vector tensorProduct ( Vector a ) for ( int i = 0 ; i < scaledVectors.length ; i++ ) { - for ( int j = 0 ; j < this.getColons() ; j++ ) + for ( int j = 0 ; j < scaledVectors[i].getColons() ; j++ ) { res.setAt( 0 , - this.getColons() * i + j , + scaledVectors[i].getColons() * i + j , scaledVectors[i].getAt( 0 , j ) ); } } diff --git a/src/test/java/com/simulation/EngineTryingOfConcurrencyTest.java b/src/test/java/com/simulation/EngineTryingOfConcurrencyTest.java new file mode 100644 index 0000000..903877e --- /dev/null +++ b/src/test/java/com/simulation/EngineTryingOfConcurrencyTest.java @@ -0,0 +1,57 @@ +/* + * 24/12/2019 13:32:13 + * EngineTryingOfConcurrencyTest.java created by Tsvetelin + */ +package com.simulation; + +import java.util.concurrent.Future; + +import com.simulationQ.Engine; +import com.simulationQ.SimulationRound; +import com.simulationQ.simulation.computation.qubits.Qubit; +import com.simulationQ.simulation.computation.qubits.register.QRegister; + +/** + * @author Tsvetelin + * + */ +public class EngineTryingOfConcurrencyTest +{ + + /** + * + */ + public EngineTryingOfConcurrencyTest () + { + // TODO Auto-generated constructor stub + } + + /** + * @param args + */ + public static void main ( String [] args ) + { + try ( final Engine eng = Engine.instance ) + { + final QRegister reg = new QRegister( new Qubit[] { + Qubit.QUBIT_HALF_HALF, Qubit.QUBIT_HALF_HALF + } ); + + final SimulationRound round = new SimulationRound( reg , 100_000 ); + +// QRegister res = round.runSimulation(); +// +// System.out.println( "Result blocking: " + res ); + + Future< QRegister > resAsync = eng.runSimulation( round ); + + while ( !resAsync.isDone() ) {} // waiting for execution + + System.out.println( "Result concurrent: " + resAsync.get() ); + + } catch ( Exception e ) + { + e.printStackTrace(); + } + } +} From 739315742d80264f3072c3ffb2cca264545eca1e Mon Sep 17 00:00:00 2001 From: Tsvetelin Kostadinov Date: Wed, 26 Feb 2020 21:57:04 +0200 Subject: [PATCH 7/9] Fixed the application of a gate to a register --- src/main/java/com/simulationQ/Main.java | 27 ++-- .../computation/QFinalStateCalculator.java | 3 +- .../simulation/computation/gates/QGate.java | 36 +----- .../computation/gates/QGateApplier.java | 120 ++++++++++++++++++ .../qubits/register/QRegister.java | 4 +- .../com/simulation/ExtendingMatricesTest.java | 25 ++++ .../java/com/simulation/QGateApplierTest.java | 36 ++++++ 7 files changed, 206 insertions(+), 45 deletions(-) create mode 100644 src/main/java/com/simulationQ/simulation/computation/gates/QGateApplier.java create mode 100644 src/test/java/com/simulation/ExtendingMatricesTest.java create mode 100644 src/test/java/com/simulation/QGateApplierTest.java diff --git a/src/main/java/com/simulationQ/Main.java b/src/main/java/com/simulationQ/Main.java index 2c23364..5474467 100644 --- a/src/main/java/com/simulationQ/Main.java +++ b/src/main/java/com/simulationQ/Main.java @@ -17,21 +17,28 @@ public class Main */ public Main () {} - + /** * * @param args + * @throws Exception */ - public static void main ( String [] args ) + public static void main ( String [] args ) throws Exception { + try { - switch( args[0] ) - { - case "gui": startInGUIMode(args); break; - case "nogui": startInNOGUIMode(args); break; - default: System.out.println( "Unexpected command: " + args[0] ); - } + switch ( args[0] ) + { + case "gui" : + startInGUIMode( args ); + break; + case "nogui" : + startInNOGUIMode( args ); + break; + default : + System.out.println( "Unexpected command: " + args[0] ); + } } catch ( IndexOutOfBoundsException e ) { System.out.println( "You must specify the mode first - gui/nogui" ); @@ -44,7 +51,7 @@ public static void main ( String [] args ) private static void startInGUIMode ( String [] args ) { System.out.println( "String GUI" ); - + } /** @@ -54,5 +61,5 @@ private static void startInNOGUIMode ( String [] args ) { System.out.println( "String NOGUI" ); } - + } diff --git a/src/main/java/com/simulationQ/simulation/computation/QFinalStateCalculator.java b/src/main/java/com/simulationQ/simulation/computation/QFinalStateCalculator.java index 1dfb355..de4609e 100644 --- a/src/main/java/com/simulationQ/simulation/computation/QFinalStateCalculator.java +++ b/src/main/java/com/simulationQ/simulation/computation/QFinalStateCalculator.java @@ -8,6 +8,7 @@ import java.util.List; import com.simulationQ.simulation.computation.gates.QGate; +import com.simulationQ.simulation.computation.gates.QGateApplier; import com.simulationQ.simulation.computation.qubits.register.QRegister; @@ -24,7 +25,7 @@ public static QRegister calculateFinalState ( List< QGate > gates , return gates.stream() .reduce( startState , - ( state , gate ) -> gate.apply( state ) , + ( state , gate ) -> QGateApplier.apply( gate , state ) , ( oldState , newState ) -> newState ); } diff --git a/src/main/java/com/simulationQ/simulation/computation/gates/QGate.java b/src/main/java/com/simulationQ/simulation/computation/gates/QGate.java index a2381c9..5a0390d 100644 --- a/src/main/java/com/simulationQ/simulation/computation/gates/QGate.java +++ b/src/main/java/com/simulationQ/simulation/computation/gates/QGate.java @@ -7,11 +7,7 @@ import java.util.Objects; -import com.simulationQ.simulation.computation.qubits.Qubit; -import com.simulationQ.simulation.computation.qubits.register.QRegister; import com.simulationQ.simulation.util.math.matrices.Matrix; -import com.simulationQ.simulation.util.math.matrices.MatrixOperations; -import com.simulationQ.simulation.util.math.matrices.vectors.Vector; /** @@ -23,7 +19,7 @@ public abstract class QGate private final Matrix operation; - private final int numberInputBits; + private final int numberInputCoeficients; private final String informationForGate; @@ -49,7 +45,7 @@ public QGate ( Matrix operation , throw new IllegalArgumentException( " Cannot construct gate with the given input " ); } - this.numberInputBits = numberInputBits; + this.numberInputCoeficients = numberInputBits; this.operation = operation; this.informationForGate = information; } @@ -65,9 +61,9 @@ public Matrix getOperation () /** * @return the numberInputBits */ - public int getNumberInputBits () + public int getNumberInputCoeficients () { - return numberInputBits; + return numberInputCoeficients; } /** @@ -78,30 +74,6 @@ public String getInformationForGate () return informationForGate; } - public QRegister apply ( QRegister reg ) - { - if ( reg.size() != this.numberInputBits ) - throw new IllegalArgumentException( "The register must be with the same number of bits as the input of the gate!" ); - - if ( this.numberInputBits == 1 ) - { - - final Vector qubitVector = reg.getComputationalVector(); - - final Vector res = MatrixOperations.multiply( this.operation , - qubitVector ); - - return new QRegister( new Qubit[] { - new Qubit( res.getAt( 0 ) , res.getAt( 1 ) ) } ); - - } else - { - // TODO Implement more bits in the gates - throw new UnsupportedOperationException( "More bits are not as of yet supported!" ); - } - - } - /** * @param operation2 * @param numberInputBits2 diff --git a/src/main/java/com/simulationQ/simulation/computation/gates/QGateApplier.java b/src/main/java/com/simulationQ/simulation/computation/gates/QGateApplier.java new file mode 100644 index 0000000..eb48653 --- /dev/null +++ b/src/main/java/com/simulationQ/simulation/computation/gates/QGateApplier.java @@ -0,0 +1,120 @@ +/* + * 24/12/2019 14:43:19 + * QGateApplier.java created by Tsvetelin + */ +package com.simulationQ.simulation.computation.gates; + + +import java.util.Objects; + +import com.simulationQ.simulation.computation.qubits.Qubit; +import com.simulationQ.simulation.computation.qubits.register.QRegister; +import com.simulationQ.simulation.util.math.QMath; +import com.simulationQ.simulation.util.math.matrices.Matrix; +import com.simulationQ.simulation.util.math.matrices.MatrixOperations; +import com.simulationQ.simulation.util.math.matrices.vectors.Vector; + + +/** + * @author Tsvetelin + * + */ +public interface QGateApplier +{ + + public static QRegister apply ( final QGate gate , + final QRegister reg , + final int startingVerticalIndex ) + { + Objects.requireNonNull( gate ); + Objects.requireNonNull( reg ); + + if ( startingVerticalIndex + + gate.getOperation().getRows() > QMath.pow( 2 , reg.size() ) ) + throw new IllegalArgumentException( "The starting index cannot be such that the operation cannot fit!" ); + + final Matrix extended = extendMatrixForWholeRegister( gate.getOperation() , + QMath.pow( 2 , + startingVerticalIndex ) , + QMath.pow( 2 , + reg.size() ) ); + + System.out.println( "Adapted: " ); + System.out.println( extended ); + + Vector res = MatrixOperations.multiply( extended , reg.getComputationalVector() ); + + return new QRegister( res ); + } + + /** + * @param gate + * - the gate to apply it should be the appropriate size + * @param reg + * @return + */ + public static QRegister apply ( final QGate gate , final QRegister reg ) + { + if ( gate.getNumberInputCoeficients() != reg.getComputationalVector() + .size() ) + throw new IllegalArgumentException( "Cannot apply a gate that is not stretched" ); + + Vector res = new Vector( reg.getComputationalVector() + .multiply( gate.getOperation() ) + .getMatrix()[0] ); + + return new QRegister( res ); + } + + public static Qubit apply ( final QGate gate , final Qubit qubit ) + { + final Vector qbit = qubit.getAsVector(); + final Matrix operation = gate.getOperation(); + + final Vector res = MatrixOperations.multiply( operation , qbit ); + + return new Qubit( res.getAt( 0 ) , res.getAt( 1 ) ); + } + + public static QRegister apply ( final Matrix operation , + final QRegister reg ) throws Exception + { + final Vector vec = reg.getComputationalVector(); + + final Vector res = MatrixOperations.multiply( operation , vec ); + + return new QRegister( res ); + } + + /** + * @param operation + * @param startingVerticalIndex + * @param sizeOfRegister + * @return + */ + public static Matrix extendMatrixForWholeRegister ( final Matrix operation , + final int startingVerticalIndex , + final int sizeOfRegister ) + { + + Matrix extended = Matrix.multiplicativeIdentity( 2 , 2 ); + final Matrix ident_2x2 = Matrix.multiplicativeIdentity( 2 , 2 ); + + for ( int i = 1 ; i < startingVerticalIndex ; i++ ) + { + extended = MatrixOperations.productKronecker( extended , + ident_2x2 ); + } + + extended = MatrixOperations.productKronecker( operation , + extended); + + while ( extended.getColons() < sizeOfRegister ) + { + extended = MatrixOperations.productKronecker( extended , + ident_2x2 ); + } + + return extended; + } +} diff --git a/src/main/java/com/simulationQ/simulation/computation/qubits/register/QRegister.java b/src/main/java/com/simulationQ/simulation/computation/qubits/register/QRegister.java index b6db76b..fc1fc4a 100644 --- a/src/main/java/com/simulationQ/simulation/computation/qubits/register/QRegister.java +++ b/src/main/java/com/simulationQ/simulation/computation/qubits/register/QRegister.java @@ -18,8 +18,8 @@ public class QRegister { private final Vector computationalVector; - - public QRegister ( Qubit [] qubits ) + + public QRegister ( Qubit... qubits ) { this.computationalVector = getAsComputationalVector( qubits ); } diff --git a/src/test/java/com/simulation/ExtendingMatricesTest.java b/src/test/java/com/simulation/ExtendingMatricesTest.java new file mode 100644 index 0000000..d850280 --- /dev/null +++ b/src/test/java/com/simulation/ExtendingMatricesTest.java @@ -0,0 +1,25 @@ +/* + * 26/02/2020 15:28:43 + * ExtendingMatricesTest.java created by Tsvetelin + */ +package com.simulation; + +import com.simulationQ.simulation.computation.gates.impl.NOT; +import com.simulationQ.simulation.util.math.matrices.Matrix; +import com.simulationQ.simulation.util.math.matrices.MatrixOperations; + +/** + * @author Tsvetelin + * + */ +public abstract class ExtendingMatricesTest +{ + public static void main ( String [] args ) + { + final Matrix oper = NOT.OPERATION_MATRIX; + final Matrix identity = Matrix.multiplicativeIdentity( oper.getRows() , oper.getColons() ); + + System.out.println( MatrixOperations.productKronecker( oper , identity ) ); + + } +} diff --git a/src/test/java/com/simulation/QGateApplierTest.java b/src/test/java/com/simulation/QGateApplierTest.java new file mode 100644 index 0000000..a0deef4 --- /dev/null +++ b/src/test/java/com/simulation/QGateApplierTest.java @@ -0,0 +1,36 @@ +/* + * 26/02/2020 15:12:59 + * QGateApplierTest.java created by Tsvetelin + */ +package com.simulation; + +import com.simulationQ.simulation.computation.gates.QGate; +import com.simulationQ.simulation.computation.gates.QGateApplier; +import com.simulationQ.simulation.computation.gates.impl.Hadamard; +import com.simulationQ.simulation.computation.qubits.Qubit; + +/** + * @author Tsvetelin + * + */ +public abstract class QGateApplierTest +{ + + public static void main ( String [] args ) + { + final Qubit on = Qubit.QUBIT_ON; + final Qubit off = Qubit.QUBIT_OFF; + + final QGate oper = new Hadamard(); + + System.out.println( "------ H * |1>" ); + System.out.println( QGateApplier.apply( oper , on ) ); + + System.out.println( ); + + System.out.println( "------ H * |0>" ); + System.out.println( QGateApplier.apply( oper , off ) ); + + } + +} From 4ddfc1d5a73721eaf7124a10d2f5df781213736f Mon Sep 17 00:00:00 2001 From: Tsvetelin Kostadinov Date: Wed, 4 Mar 2020 14:28:42 +0200 Subject: [PATCH 8/9] Sorted out problems when applying gate to a register --- src/main/java/com/simulationQ/Mediator.java | 28 ++--- .../computation/QFinalStateCalculator.java | 19 +-- .../computation/gates/QGateApplier.java | 112 +++++++++++------- .../computation/program/QProgram.java | 90 ++++++++++++++ .../computation/program/package-info.java | 9 ++ .../util/math/matrices/MatrixOperations.java | 5 + .../java/com/simulation/QGateApplierTest.java | 4 +- .../java/com/simulation/QProgramTesting.java | 38 ++++++ 8 files changed, 231 insertions(+), 74 deletions(-) create mode 100644 src/main/java/com/simulationQ/simulation/computation/program/QProgram.java create mode 100644 src/main/java/com/simulationQ/simulation/computation/program/package-info.java create mode 100644 src/test/java/com/simulation/QProgramTesting.java diff --git a/src/main/java/com/simulationQ/Mediator.java b/src/main/java/com/simulationQ/Mediator.java index f0c3f28..c7fb86c 100644 --- a/src/main/java/com/simulationQ/Mediator.java +++ b/src/main/java/com/simulationQ/Mediator.java @@ -5,14 +5,6 @@ package com.simulationQ; -import java.util.List; -import java.util.concurrent.Future; - -import com.simulationQ.simulation.computation.QFinalStateCalculator; -import com.simulationQ.simulation.computation.gates.QGate; -import com.simulationQ.simulation.computation.qubits.register.QRegister; - - /** * * This is the mediator between the GUI and the engine @@ -31,16 +23,16 @@ public final class Mediator private Mediator () {} - public final Future< QRegister > runSimulation ( final List< QGate > gates , - final QRegister startState , - final long rounds ) - { - final QRegister finalState = QFinalStateCalculator.calculateFinalState( gates , - startState ); - final SimulationRound round = new SimulationRound( finalState , - rounds ); - return Engine.instance.runSimulation( round ); - } +// public final Future< QRegister > runSimulation ( final List< QGate > gates , +// final QRegister startState , +// final long rounds ) +// { +// final QRegister finalState = QFinalStateCalculator.calculateFinalState( gates , +// startState ); +// final SimulationRound round = new SimulationRound( finalState , +// rounds ); +// return Engine.instance.runSimulation( round ); +// } } diff --git a/src/main/java/com/simulationQ/simulation/computation/QFinalStateCalculator.java b/src/main/java/com/simulationQ/simulation/computation/QFinalStateCalculator.java index de4609e..1afd740 100644 --- a/src/main/java/com/simulationQ/simulation/computation/QFinalStateCalculator.java +++ b/src/main/java/com/simulationQ/simulation/computation/QFinalStateCalculator.java @@ -5,10 +5,8 @@ package com.simulationQ.simulation.computation; -import java.util.List; - -import com.simulationQ.simulation.computation.gates.QGate; import com.simulationQ.simulation.computation.gates.QGateApplier; +import com.simulationQ.simulation.computation.program.QProgram; import com.simulationQ.simulation.computation.qubits.register.QRegister; @@ -19,14 +17,17 @@ public interface QFinalStateCalculator { - public static QRegister calculateFinalState ( List< QGate > gates , + public static QRegister calculateFinalState ( QProgram program , QRegister startState ) { - return gates.stream() - .reduce( startState , - ( state , gate ) -> QGateApplier.apply( gate , state ) , - ( oldState , newState ) -> newState ); + return program.stream() + .reduce( startState , + ( state , + programPart ) -> QGateApplier.apply( programPart.getOper() , + state , + programPart.getStartIndexInRegister() ) , + ( oldState , newState ) -> newState ); } - + } diff --git a/src/main/java/com/simulationQ/simulation/computation/gates/QGateApplier.java b/src/main/java/com/simulationQ/simulation/computation/gates/QGateApplier.java index eb48653..aeb1a64 100644 --- a/src/main/java/com/simulationQ/simulation/computation/gates/QGateApplier.java +++ b/src/main/java/com/simulationQ/simulation/computation/gates/QGateApplier.java @@ -32,19 +32,55 @@ public static QRegister apply ( final QGate gate , if ( startingVerticalIndex + gate.getOperation().getRows() > QMath.pow( 2 , reg.size() ) ) throw new IllegalArgumentException( "The starting index cannot be such that the operation cannot fit!" ); - + + + final Vector vectorOfReg = reg.getComputationalVector(); final Matrix extended = extendMatrixForWholeRegister( gate.getOperation() , - QMath.pow( 2 , - startingVerticalIndex ) , - QMath.pow( 2 , - reg.size() ) ); + startingVerticalIndex, + vectorOfReg.size() ); - System.out.println( "Adapted: " ); - System.out.println( extended ); - - Vector res = MatrixOperations.multiply( extended , reg.getComputationalVector() ); + + + try + { + return debugApply( extended , reg ); + } catch ( Exception e ) + { + e.printStackTrace(); + } + return null; + } - return new QRegister( res ); + /** + * @param operation + * @param startingVerticalIndex + * @param sizeOfVector + * @return + */ + public static Matrix extendMatrixForWholeRegister ( final Matrix operation , + final int startingVerticalIndex , + final int sizeOfVector ) + { + + Matrix extended = Matrix.multiplicativeIdentity( 1 , 1 ); + final Matrix ident_2x2 = Matrix.multiplicativeIdentity( 2 , 2 ); + + for ( int i = 0 ; i < startingVerticalIndex ; i++ ) + { + extended = MatrixOperations.productKronecker( extended , + ident_2x2 ); + } + + extended = MatrixOperations.productKronecker( extended , + operation); + + while ( extended.getColons() < sizeOfVector ) + { + extended = MatrixOperations.productKronecker( extended , + ident_2x2 ); + } + + return extended; } /** @@ -53,7 +89,7 @@ public static QRegister apply ( final QGate gate , * @param reg * @return */ - public static QRegister apply ( final QGate gate , final QRegister reg ) + public static QRegister applyAppropriateGate ( final QGate gate , final QRegister reg ) { if ( gate.getNumberInputCoeficients() != reg.getComputationalVector() .size() ) @@ -66,7 +102,7 @@ public static QRegister apply ( final QGate gate , final QRegister reg ) return new QRegister( res ); } - public static Qubit apply ( final QGate gate , final Qubit qubit ) + public static Qubit applyGateToQubit ( final QGate gate , final Qubit qubit ) { final Vector qbit = qubit.getAsVector(); final Matrix operation = gate.getOperation(); @@ -76,7 +112,7 @@ public static Qubit apply ( final QGate gate , final Qubit qubit ) return new Qubit( res.getAt( 0 ) , res.getAt( 1 ) ); } - public static QRegister apply ( final Matrix operation , + public static QRegister debugApply ( final Matrix operation , final QRegister reg ) throws Exception { final Vector vec = reg.getComputationalVector(); @@ -85,36 +121,22 @@ public static QRegister apply ( final Matrix operation , return new QRegister( res ); } - - /** - * @param operation - * @param startingVerticalIndex - * @param sizeOfRegister - * @return - */ - public static Matrix extendMatrixForWholeRegister ( final Matrix operation , - final int startingVerticalIndex , - final int sizeOfRegister ) - { - - Matrix extended = Matrix.multiplicativeIdentity( 2 , 2 ); - final Matrix ident_2x2 = Matrix.multiplicativeIdentity( 2 , 2 ); - - for ( int i = 1 ; i < startingVerticalIndex ; i++ ) - { - extended = MatrixOperations.productKronecker( extended , - ident_2x2 ); - } - - extended = MatrixOperations.productKronecker( operation , - extended); - - while ( extended.getColons() < sizeOfRegister ) - { - extended = MatrixOperations.productKronecker( extended , - ident_2x2 ); - } - - return extended; - } + +// public static void main ( String [] args ) +// { +// final Matrix a = NOT.OPERATION_MATRIX; +// System.out.println( "--- Index = 0 ---" ); +// System.out.println( extendMatrixForWholeRegister( a , 0 , 4 ) ); +// System.out.println( "--- Index = 1 ---" ); +// System.out.println( extendMatrixForWholeRegister( a , 1 , 4 ) ); +// +// System.out.println( ); +// System.out.println( ); +// +// final Matrix b = Hadamard.OPERATION_MATRIX; +// System.out.println( "--- Index = 0 ---" ); +// System.out.println( extendMatrixForWholeRegister( b , 0 , 4 ) ); +// System.out.println( "--- Index = 1 ---" ); +// System.out.println( extendMatrixForWholeRegister( b , 1 , 4 ) ); +// } } diff --git a/src/main/java/com/simulationQ/simulation/computation/program/QProgram.java b/src/main/java/com/simulationQ/simulation/computation/program/QProgram.java new file mode 100644 index 0000000..944e3ac --- /dev/null +++ b/src/main/java/com/simulationQ/simulation/computation/program/QProgram.java @@ -0,0 +1,90 @@ +/* + * 03/03/2020 16:28:07 + * Program.java created by Tsvetelin + */ +package com.simulationQ.simulation.computation.program; + + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +import com.simulationQ.simulation.computation.gates.QGate; + + +/** + * @author Tsvetelin + * + */ +public class QProgram implements Iterable< QProgram.QProgramPart > +{ + + private final List< QProgramPart > program = new LinkedList<>(); + + /** + * + */ + public QProgram () + {} + + + public void addPart( QGate gate , int index ) + { + program.add( new QProgramPart( gate , index ) ); + } + + + + @Override + public Iterator< QProgramPart > iterator () + { + return program.iterator(); + } + + public Stream< QProgramPart > stream() + { + return StreamSupport.stream(spliterator(), false); + } + + + + public static final class QProgramPart + { + + private final QGate oper; + + private final int startIndexInRegister; + + /** + * @param oper + * @param startIndexInRegister + */ + public QProgramPart ( QGate oper , int startIndexInRegister ) + { + super(); + this.oper = oper; + this.startIndexInRegister = startIndexInRegister; + } + + + /** + * @return the oper + */ + public QGate getOper () + { + return oper; + } + + + /** + * @return the startIndexInRegister + */ + public int getStartIndexInRegister () + { + return startIndexInRegister; + } + + } +} diff --git a/src/main/java/com/simulationQ/simulation/computation/program/package-info.java b/src/main/java/com/simulationQ/simulation/computation/program/package-info.java new file mode 100644 index 0000000..70bc9ec --- /dev/null +++ b/src/main/java/com/simulationQ/simulation/computation/program/package-info.java @@ -0,0 +1,9 @@ +/* + * 03/03/2020 16:27:56 + * package-info.java created by Tsvetelin + */ +/** + * @author Tsvetelin + * + */ +package com.simulationQ.simulation.computation.program; diff --git a/src/main/java/com/simulationQ/simulation/util/math/matrices/MatrixOperations.java b/src/main/java/com/simulationQ/simulation/util/math/matrices/MatrixOperations.java index 0f146b8..813a18a 100644 --- a/src/main/java/com/simulationQ/simulation/util/math/matrices/MatrixOperations.java +++ b/src/main/java/com/simulationQ/simulation/util/math/matrices/MatrixOperations.java @@ -121,6 +121,11 @@ public static Vector multiply ( final Matrix a , final Vector b ) { if ( a.getColons() != b.size() ) { + System.err.println( "Unable To Multiply:" ); + System.err.println( "Matrix: " ); + System.err.println( a ); + System.err.println( "Vector: " ); + System.err.println( b ); throw new IllegalArgumentException( "The number of cols in the matrix should be equal to the number of elements in the vector" ); } diff --git a/src/test/java/com/simulation/QGateApplierTest.java b/src/test/java/com/simulation/QGateApplierTest.java index a0deef4..0b74434 100644 --- a/src/test/java/com/simulation/QGateApplierTest.java +++ b/src/test/java/com/simulation/QGateApplierTest.java @@ -24,12 +24,12 @@ public static void main ( String [] args ) final QGate oper = new Hadamard(); System.out.println( "------ H * |1>" ); - System.out.println( QGateApplier.apply( oper , on ) ); + System.out.println( QGateApplier.applyGateToQubit( oper , on ) ); System.out.println( ); System.out.println( "------ H * |0>" ); - System.out.println( QGateApplier.apply( oper , off ) ); + System.out.println( QGateApplier.applyGateToQubit( oper , off ) ); } diff --git a/src/test/java/com/simulation/QProgramTesting.java b/src/test/java/com/simulation/QProgramTesting.java new file mode 100644 index 0000000..fa6ce39 --- /dev/null +++ b/src/test/java/com/simulation/QProgramTesting.java @@ -0,0 +1,38 @@ +/* + * 04/03/2020 14:26:10 + * QProgramTesting.java created by Tsvetelin + */ +package com.simulation; + +import com.simulationQ.simulation.computation.QFinalStateCalculator; +import com.simulationQ.simulation.computation.gates.impl.Hadamard; +import com.simulationQ.simulation.computation.program.QProgram; +import com.simulationQ.simulation.computation.qubits.Qubit; +import com.simulationQ.simulation.computation.qubits.register.QRegister; + +/** + * @author Tsvetelin + * + */ +public abstract class QProgramTesting +{ + + public static void main ( String [] args ) + { + final QRegister reg = new QRegister( Qubit.QUBIT_ON , Qubit.QUBIT_ON ); + final Hadamard gate = new Hadamard(); + + final QProgram program = new QProgram(); + + program.addPart( gate , 0 ); + program.addPart( gate , 1 ); + program.addPart( gate , 0 ); + program.addPart( gate , 1 ); + + final QRegister res = QFinalStateCalculator.calculateFinalState( program , reg ); + + System.out.println( res ); + + } + +} From 8a4f85afeda860d431a4c1c6fe4cac2731390ed3 Mon Sep 17 00:00:00 2001 From: Tsvetelin Kostadinov Date: Wed, 4 Mar 2020 15:50:03 +0200 Subject: [PATCH 9/9] Final commit before I f*ck it up Added names for the gates And added toString() to some classes --- src/main/java/com/presenting/Presenting.java | 161 ++++++++++++++++++ .../java/com/presenting/package-info.java | 9 + .../simulation/computation/QCollapser.java | 2 +- .../simulation/computation/gates/QGate.java | 15 +- .../computation/gates/impl/Hadamard.java | 7 +- .../computation/gates/impl/NOT.java | 10 +- .../computation/gates/impl/PauliY.java | 12 +- .../computation/gates/impl/PauliZ.java | 10 +- .../computation/program/QProgram.java | 14 +- .../qubits/register/CRegister.java | 10 +- 10 files changed, 231 insertions(+), 19 deletions(-) create mode 100644 src/main/java/com/presenting/Presenting.java create mode 100644 src/main/java/com/presenting/package-info.java diff --git a/src/main/java/com/presenting/Presenting.java b/src/main/java/com/presenting/Presenting.java new file mode 100644 index 0000000..cc457b5 --- /dev/null +++ b/src/main/java/com/presenting/Presenting.java @@ -0,0 +1,161 @@ +/* + * 04/03/2020 14:30:17 + * Presenting.java created by Tsvetelin + */ +package com.presenting; + + +import java.util.LinkedList; +import java.util.List; + +import com.simulationQ.simulation.computation.QCollapser; +import com.simulationQ.simulation.computation.QFinalStateCalculator; +import com.simulationQ.simulation.computation.gates.QGate; +import com.simulationQ.simulation.computation.gates.impl.Hadamard; +import com.simulationQ.simulation.computation.gates.impl.NOT; +import com.simulationQ.simulation.computation.gates.impl.PauliY; +import com.simulationQ.simulation.computation.gates.impl.PauliZ; +import com.simulationQ.simulation.computation.program.QProgram; +import com.simulationQ.simulation.computation.qubits.Qubit; +import com.simulationQ.simulation.computation.qubits.register.CRegister; +import com.simulationQ.simulation.computation.qubits.register.QRegister; + + +/** + * @author Tsvetelin + * + */ +public abstract class Presenting +{ + + // допустими символи 1 и 0 + // за всички останали недопустими символи се генерира грешка + + public static final String REGISTER = "010"; + + + + // допустими символи H, X, Y, Z + // разстояния и долни черти се игнорират, + // за всички останали недопустими символи се генерира грешка + + public static final String [] PROGRAM = new String[] { + "", + "", + "" + }; + + + public static final long COLLAPSES = 1_000_000L; + + /** + * @param args + */ + public static void main ( String [] args ) + { + final QRegister register = new QRegister( parseRegister() ); + System.out.println( "Start: " + register ); + + final QProgram program = new QProgram(); + + final QGate [] [] gates = parseProgram(); + + for ( int index = 0 ; index < gates.length ; index++ ) + { + for ( int j = 0 ; j < gates[index].length ; j++ ) + { + program.addPart( gates[index][j] , index ); + } + } + + final QRegister finalState = QFinalStateCalculator.calculateFinalState( program , register ); + System.out.println( "Final state: " + finalState ); + + long start = System.currentTimeMillis(); + + final CRegister result = QCollapser.collapse( finalState , COLLAPSES ); + + long end = System.currentTimeMillis(); + + System.out.println( "Result(after "+COLLAPSES+" collapses) : " + result ); + System.out.println( "Took: " + (end-start) + "ms" ); + + } + + /** + * @return + */ + private static final QGate [] [] parseProgram () + { + final String registerAndProgramNotTheSameSizeMessage = "Размерността на регистърът и на първото измерение на програмата трябва да съвпадат"; + final String unexpectedSymbolMessage = "Недопустим символ на ред %s - %s"; + if ( PROGRAM.length != REGISTER.length() ) + throw new IllegalArgumentException( registerAndProgramNotTheSameSizeMessage ); + + List< QGate [] > programRows = new LinkedList<>(); + + for ( String row : PROGRAM ) + { + List< QGate > currentRow = new LinkedList<>(); + for ( char symbol : row.toCharArray() ) + { + switch ( symbol ) + { + case 'H' : + case 'h' : + currentRow.add( new Hadamard() ); + break; + case 'X' : + case 'x' : + currentRow.add( new NOT() ); + break; + case 'Y' : + case 'y' : + currentRow.add( new PauliY() ); + break; + case 'Z' : + case 'z' : + currentRow.add( new PauliZ() ); + break; + case ' ' : + case '_' : + break; + default : + throw new IllegalArgumentException( String.format( unexpectedSymbolMessage , + row , + symbol ) ); + } + } + programRows.add( currentRow.toArray( new QGate[currentRow.size()] ) ); + } + + return programRows.toArray( new QGate[programRows.size()][] ); + + } + + /** + * @return + */ + private static final Qubit [] parseRegister () + { + final String unexpectedSymbolMessage = "Регистърът трябва да съдържа само символи 1 или 0"; + + final List< Qubit > reg = new LinkedList< Qubit >(); + for ( char bit : REGISTER.toCharArray() ) + { + switch ( bit ) + { + case '0' : + reg.add( Qubit.QUBIT_OFF ); + break; + case '1' : + reg.add( Qubit.QUBIT_ON ); + break; + default : + throw new IllegalArgumentException( unexpectedSymbolMessage ); + } + } + return reg.toArray( new Qubit[reg.size()] ); + } + +} diff --git a/src/main/java/com/presenting/package-info.java b/src/main/java/com/presenting/package-info.java new file mode 100644 index 0000000..d935cee --- /dev/null +++ b/src/main/java/com/presenting/package-info.java @@ -0,0 +1,9 @@ +/* + * 04/03/2020 14:29:53 + * package-info.java created by Tsvetelin + */ +/** + * @author Tsvetelin + * + */ +package com.presenting; diff --git a/src/main/java/com/simulationQ/simulation/computation/QCollapser.java b/src/main/java/com/simulationQ/simulation/computation/QCollapser.java index ce6229a..32734e9 100644 --- a/src/main/java/com/simulationQ/simulation/computation/QCollapser.java +++ b/src/main/java/com/simulationQ/simulation/computation/QCollapser.java @@ -166,7 +166,7 @@ public static String collapseToString ( final QRegister reg ) * @param rounds * @return */ - public static QRegister collapse ( final QRegister reg , final long rounds ) + public static CRegister collapse ( final QRegister reg , final long rounds ) { final int [] counters = Arrays.stream( "0".repeat( QMath.pow( 2 , reg.size() ) ) .split( "" ) ) diff --git a/src/main/java/com/simulationQ/simulation/computation/gates/QGate.java b/src/main/java/com/simulationQ/simulation/computation/gates/QGate.java index 5a0390d..2b28386 100644 --- a/src/main/java/com/simulationQ/simulation/computation/gates/QGate.java +++ b/src/main/java/com/simulationQ/simulation/computation/gates/QGate.java @@ -16,7 +16,8 @@ */ public abstract class QGate { - + private final String name; + private final Matrix operation; private final int numberInputCoeficients; @@ -30,7 +31,8 @@ public abstract class QGate * @param periodOfOperation * @param information */ - public QGate ( Matrix operation , + public QGate ( String name , + Matrix operation , int numberInputBits , int periodOfOperation , String information ) @@ -44,7 +46,8 @@ public QGate ( Matrix operation , { throw new IllegalArgumentException( " Cannot construct gate with the given input " ); } - + + this.name = name; this.numberInputCoeficients = numberInputBits; this.operation = operation; this.informationForGate = information; @@ -100,4 +103,10 @@ private static final boolean isMatrixSquare ( Matrix a ) { return a.getRows() == a.getColons(); } + + @Override + public String toString () + { + return this.name; + } } diff --git a/src/main/java/com/simulationQ/simulation/computation/gates/impl/Hadamard.java b/src/main/java/com/simulationQ/simulation/computation/gates/impl/Hadamard.java index 389fb93..a1117e5 100644 --- a/src/main/java/com/simulationQ/simulation/computation/gates/impl/Hadamard.java +++ b/src/main/java/com/simulationQ/simulation/computation/gates/impl/Hadamard.java @@ -4,10 +4,12 @@ */ package com.simulationQ.simulation.computation.gates.impl; + import com.simulationQ.simulation.computation.gates.QGate; import com.simulationQ.simulation.util.math.complexNumbers.ComplexNumber; import com.simulationQ.simulation.util.math.matrices.Matrix; + /** * @author Tsvetelin * @@ -15,6 +17,8 @@ public class Hadamard extends QGate { + public static final String NAME = "Hadamard"; + public static final Matrix OPERATION_MATRIX = new Matrix( new ComplexNumber[][] { { ComplexNumber.REAL_UNIT, ComplexNumber.REAL_UNIT }, { ComplexNumber.REAL_UNIT, ComplexNumber.REAL_UNIT.negate() } @@ -36,7 +40,8 @@ public class Hadamard extends QGate */ public Hadamard () { - super( Hadamard.OPERATION_MATRIX , + super( Hadamard.NAME , + Hadamard.OPERATION_MATRIX , Hadamard.NUMBER_OF_INPUT_BITS , Hadamard.PERIOD , Hadamard.INFORMATION ); diff --git a/src/main/java/com/simulationQ/simulation/computation/gates/impl/NOT.java b/src/main/java/com/simulationQ/simulation/computation/gates/impl/NOT.java index 175efe9..c24c9c0 100644 --- a/src/main/java/com/simulationQ/simulation/computation/gates/impl/NOT.java +++ b/src/main/java/com/simulationQ/simulation/computation/gates/impl/NOT.java @@ -4,10 +4,12 @@ */ package com.simulationQ.simulation.computation.gates.impl; + import com.simulationQ.simulation.computation.gates.QGate; import com.simulationQ.simulation.util.math.complexNumbers.ComplexNumber; import com.simulationQ.simulation.util.math.matrices.Matrix; + /** * @author Tsvetelin * @@ -15,6 +17,8 @@ public class NOT extends QGate { + public static final String NAME = "Pauli-X/NOT"; + public static final Matrix OPERATION_MATRIX = new Matrix( new ComplexNumber[][] { { ComplexNumber.ORIGIN, ComplexNumber.REAL_UNIT }, { ComplexNumber.REAL_UNIT, ComplexNumber.ORIGIN } @@ -34,7 +38,11 @@ public class NOT extends QGate */ public NOT () { - super( OPERATION_MATRIX , NUMBER_OF_INPUT_BITS , PERIOD , INFORMATION ); + super( NAME , + OPERATION_MATRIX , + NUMBER_OF_INPUT_BITS , + PERIOD , + INFORMATION ); } } diff --git a/src/main/java/com/simulationQ/simulation/computation/gates/impl/PauliY.java b/src/main/java/com/simulationQ/simulation/computation/gates/impl/PauliY.java index 64d682a..ec98774 100644 --- a/src/main/java/com/simulationQ/simulation/computation/gates/impl/PauliY.java +++ b/src/main/java/com/simulationQ/simulation/computation/gates/impl/PauliY.java @@ -4,10 +4,12 @@ */ package com.simulationQ.simulation.computation.gates.impl; + import com.simulationQ.simulation.computation.gates.QGate; import com.simulationQ.simulation.util.math.complexNumbers.ComplexNumber; import com.simulationQ.simulation.util.math.matrices.Matrix; + /** * @author Tsvetelin * @@ -15,6 +17,8 @@ public class PauliY extends QGate { + public static final String NAME = "Pauli-Y"; + public static final Matrix OPERATION_MATRIX = new Matrix( new ComplexNumber[][] { { ComplexNumber.ORIGIN, ComplexNumber.IMAG_UNIT.negate() }, { ComplexNumber.IMAG_UNIT, ComplexNumber.ORIGIN } @@ -25,10 +29,14 @@ public class PauliY extends QGate public static final int PERIOD = 1; public static final String INFORMATION = "Flips the bloch sphere around the y axis"; - + public PauliY () { - super( OPERATION_MATRIX , NUMBER_OF_INPUT_BITS , PERIOD , INFORMATION ); + super( NAME , + OPERATION_MATRIX , + NUMBER_OF_INPUT_BITS , + PERIOD , + INFORMATION ); } } diff --git a/src/main/java/com/simulationQ/simulation/computation/gates/impl/PauliZ.java b/src/main/java/com/simulationQ/simulation/computation/gates/impl/PauliZ.java index 7015bf3..7ee4d2c 100644 --- a/src/main/java/com/simulationQ/simulation/computation/gates/impl/PauliZ.java +++ b/src/main/java/com/simulationQ/simulation/computation/gates/impl/PauliZ.java @@ -4,10 +4,12 @@ */ package com.simulationQ.simulation.computation.gates.impl; + import com.simulationQ.simulation.computation.gates.QGate; import com.simulationQ.simulation.util.math.complexNumbers.ComplexNumber; import com.simulationQ.simulation.util.math.matrices.Matrix; + /** * @author Tsvetelin * @@ -15,6 +17,8 @@ public class PauliZ extends QGate { + public static final String NAME = "Pauli-Z"; + public static final Matrix OPERATION_MATRIX = new Matrix( new ComplexNumber[][] { { ComplexNumber.REAL_UNIT, ComplexNumber.ORIGIN }, { ComplexNumber.ORIGIN, ComplexNumber.REAL_UNIT.negate() } @@ -28,7 +32,11 @@ public class PauliZ extends QGate public PauliZ () { - super( OPERATION_MATRIX , NUMBER_OF_INPUT_BITS , PERIOD , INFORMATION ); + super( NAME , + OPERATION_MATRIX , + NUMBER_OF_INPUT_BITS , + PERIOD , + INFORMATION ); } } diff --git a/src/main/java/com/simulationQ/simulation/computation/program/QProgram.java b/src/main/java/com/simulationQ/simulation/computation/program/QProgram.java index 944e3ac..bb369c7 100644 --- a/src/main/java/com/simulationQ/simulation/computation/program/QProgram.java +++ b/src/main/java/com/simulationQ/simulation/computation/program/QProgram.java @@ -85,6 +85,18 @@ public int getStartIndexInRegister () { return startIndexInRegister; } - + + @Override + public String toString () + { + return oper.toString(); + } + + } + + @Override + public String toString () + { + return program.toString(); } } diff --git a/src/main/java/com/simulationQ/simulation/computation/qubits/register/CRegister.java b/src/main/java/com/simulationQ/simulation/computation/qubits/register/CRegister.java index 921db82..abad856 100644 --- a/src/main/java/com/simulationQ/simulation/computation/qubits/register/CRegister.java +++ b/src/main/java/com/simulationQ/simulation/computation/qubits/register/CRegister.java @@ -6,7 +6,6 @@ import com.simulationQ.simulation.computation.qubits.Qubit; -import com.simulationQ.simulation.util.math.complexNumbers.ComplexNumber; /** @@ -57,14 +56,7 @@ public CRegister ( final String bits ) @Override public String toString () { - StringBuilder sb = new StringBuilder(); - - for ( ComplexNumber bit : this.getComputationalVector() ) - { - sb.append( bit.equals( ComplexNumber.REAL_UNIT ) ? '1' : '0' ); - } - - return sb.toString(); + return this.state; } }