From 7c5350d9fe216a1b22459c4e0546137edea1043c Mon Sep 17 00:00:00 2001 From: NAKATA Maho Date: Mon, 14 Feb 2022 12:47:09 +0900 Subject: [PATCH] Cuquantum backend (#84) * Add Docker of cuStatevector version (WIP) * Small fixes * update Dockerfile * Now use qsim as the backend. Looks like cuquantum is too primitive at the moment and cannot install outside of conda. * WIP: cannot install cuQuantum at the moment * . * a small fix and now cuquantum builds. * Cleanups. * qsim_nvidia -> cirq * fix cuQuantum download url * add pycall for gem * move * . * avoid the following error: UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-2: ordinal not in range(128) * test circuit * . * Update qni-cirqbridge. * qsim GPU support requires to build from the source * Updated cirqbridge. qsimcirq now runs on the GPU. * Remove empty circuits. * . * . * rename to cirq to cirq_backend * Solve conflicts when merging from upstream * Solve conflicts when merging from upstream * back to my backend. * . * backout to my cirqbridge. * . * Impliment |0>, |1> initializations and H gate. Co-authored-by: Yasuhito Takamiya --- Dockerfile_cirq_backend | 101 ++++++++++++++++++++++++++++++++++++++ Dockerfile_cuquantum | 105 ++++++++++++++++++++++++++++++++++++++++ apps/www/Gemfile | 1 + apps/www/Gemfile.lock | 2 + apps/www/lib/cirq.rb | 103 +++++++++++++++++++++++++++++++++++++-- 5 files changed, 307 insertions(+), 5 deletions(-) create mode 100644 Dockerfile_cirq_backend create mode 100644 Dockerfile_cuquantum diff --git a/Dockerfile_cirq_backend b/Dockerfile_cirq_backend new file mode 100644 index 000000000..9c7f0c19e --- /dev/null +++ b/Dockerfile_cirq_backend @@ -0,0 +1,101 @@ +# 1. The Qniapp is built as follows: +# $ git clone https://github.com/qniapp/qni.git +# $ cd qni +# $ docker build -f Dockerfile_cirq_backend . -t qni_cirq_backend +# 2. Then run by: +# $ docker run -p 3000:3000 --gpus all --rm -it qni_cirq_backend +# 3. access http://127.0.0.1:3000 in your browser + +# Troubleshooting +# If the port 3000 is already used, change 3000 to 4000 (for example) +# $ docker run -p 4000:3000 --gpus all --rm -it qni_cirq_backend +# and access http://127.0.0.1:4000 in your browser + +FROM nvidia/cuda:11.5.1-devel-ubuntu20.04 + +RUN apt update +RUN apt -y upgrade +RUN apt install -y sudo +ENV DEBIAN_FRONTEND=noninteractive +RUN apt install -y tzdata +# set your timezone +ENV TZ Asia/Tokyo +RUN echo "${TZ}" > /etc/timezone \ + && rm /etc/localtime \ + && ln -s /usr/share/zoneinfo/Asia/Tokyo /etc/localtime \ + && dpkg-reconfigure -f noninteractive tzdata + +RUN apt install -y build-essential +RUN apt install -y git wget time curl libssl-dev zlib1g-dev libpq-dev +RUN apt install -y redis-server +RUN apt install -y ng-common ng-cjk emacs-nox +RUN apt install -y postgresql postgresql-contrib + +## cuda +RUN apt -y install cuda-drivers +RUN wget https://developer.download.nvidia.com/compute/cuquantum/redist/cuquantum/linux-x86_64/cuquantum-linux-x86_64-0.1.0.30-archive.tar.xz +RUN tar xvfJ cuquantum-linux-x86_64-0.1.0.30-archive.tar.xz -C /tmp +RUN cd /tmp/cuquantum-linux-x86_64-0.1.0.30-archive ; tar cf - . | (cd /usr/local; tar vxf -) + +## node.js +RUN apt install -y nodejs npm && npm install n -g && n stable && apt purge -y nodejs npm + +## npm +RUN curl -qL https://www.npmjs.com/install.sh | sh + +## yarn +RUN npm install -g yarn + +## ruby +RUN wget https://cache.ruby-lang.org/pub/ruby/2.7/ruby-2.7.4.tar.gz && tar xvfz ruby-2.7.4.tar.gz && cd ruby-2.7.4 && ./configure && make && make install + +## python3 +RUN apt install -y python3 python3-pip +RUN sudo update-alternatives --install /usr/bin/python python /usr/bin/python3 1 + +ARG DOCKER_UID=1000 +ARG DOCKER_USER=docker +ARG DOCKER_PASSWORD=docker +RUN useradd -u $DOCKER_UID -m $DOCKER_USER --shell /bin/bash && echo "$DOCKER_USER:$DOCKER_PASSWORD" | chpasswd && echo "$DOCKER_USER ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers + +USER ${DOCKER_USER} +RUN echo "\n\ +[user]\n\ + email = ${GIT_EMAIL}\n\ + name = ${GIT_NAME}\n\ +" > /home/$DOCKER_USER/.gitconfig + +SHELL ["/bin/bash", "-l", "-c"] + +# qsim +ENV PATH=/usr/local/cuda/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/home/$DOCKER_USER/.rbenv/bin:/home/$DOCKER_USER/.local/bin +# for qsimcirq +ENV CUQUANTUM_DIR=/usr/local +RUN cd /home/$DOCKER_USER && pip3 install pybind11 pytest numpy sympy cirq +RUN cd /home/$DOCKER_USER && git clone https://github.com/quantumlib/qsim.git && cd qsim && make # && make run-py-tests # to use gpu qsim must be build locally + +RUN cd /home/$DOCKER_USER && echo "cd /home/$DOCKER_USER" >> ~/.bashrc +RUN cd /home/$DOCKER_USER && git clone https://github.com/rbenv/rbenv.git ~/.rbenv +RUN cd /home/$DOCKER_USER && git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build +RUN cd /home/$DOCKER_USER && echo "export PATH=$PATH:$HOME/.rbenv/bin" >> ~/.bashrc +RUN cd /home/$DOCKER_USER && echo "export CUQUANTUM_DIR=/usr/local" >> ~/.bashrc +RUN cd /home/$DOCKER_USER && echo "export LD_LIBRARY_PATH=${CUQUANTUM_DIR}/lib64:${LD_LIBRARY_PATH}" >> ~/.bashrc +RUN cd /home/$DOCKER_USER && git clone https://github.com/nakatamaho/qni.git +RUN cd /home/$DOCKER_USER && cd qni && git fetch origin cuquantum-backend +RUN cd /home/$DOCKER_USER && cd qni && git checkout cuquantum-backend + +## settings for rails +RUN cd /home/$DOCKER_USER && cd qni/apps/www && bundle config set path 'vendor/cache' && bundle install && yarn install +RUN cd /home/$DOCKER_USER && cd qni && yarn build && cd apps/www && ./bin/rails css:build && ./bin/rails javascript:build + +## settings for postgresql +RUN sudo -u postgres service postgresql start && sudo -u postgres psql --command "CREATE USER docker WITH SUPERUSER PASSWORD 'docker';" && sudo -u postgres createdb -O docker docker +RUN cd /home/$DOCKER_USER && sudo -u postgres service postgresql start && cd qni/apps/www && ./bin/rails db:create && ./bin/rails db:migrate && ./bin/rails db:fixtures:load + +RUN cd /home/$DOCKER_USER && echo -e "#!/usr/bin/env bash\n\ +export PYTHONPATH=/home/docker/qsim/ \n\ +sudo -u postgres service postgresql start \n\ +cd /home/${DOCKER_USER} ; source ~/.bashrc ; cd qni/apps/www \n\ +./bin/rails s -b 0.0.0.0" > /tmp/startup.sh +RUN chmod 744 /tmp/startup.sh +#ENTRYPOINT ["/bin/sh", "-c", "/tmp/startup.sh"] diff --git a/Dockerfile_cuquantum b/Dockerfile_cuquantum new file mode 100644 index 000000000..6a953e17f --- /dev/null +++ b/Dockerfile_cuquantum @@ -0,0 +1,105 @@ +# 1. The Qniapp is built as follows: +# $ git clone https://github.com/qniapp/qni.git +# $ cd qni +# $ docker build -f Dockerfile_cuquantum . -t qni_cuquantum +# 2. Then run by: +# $ docker run -p 3000:3000 --gpus all --rm -it qni_cuquantum +# 3. access http://127.0.0.1:3000 in your browser + +# Troubleshooting +# If the port 3000 is already used, change 3000 to 4000 (for example) +# $ docker run -p 4000:3000 --gpus all --rm -it qni_cuquantum +# and access http://127.0.0.1:4000 in your browser + +FROM nvidia/cuda:11.5.1-devel-ubuntu20.04 + +RUN apt update +RUN apt -y upgrade +RUN apt install -y sudo +ENV DEBIAN_FRONTEND=noninteractive +RUN apt install -y tzdata +# set your timezone +ENV TZ Asia/Tokyo +RUN echo "${TZ}" > /etc/timezone \ + && rm /etc/localtime \ + && ln -s /usr/share/zoneinfo/Asia/Tokyo /etc/localtime \ + && dpkg-reconfigure -f noninteractive tzdata + +RUN apt install -y build-essential +RUN apt install -y git wget time curl libssl-dev zlib1g-dev libpq-dev +RUN apt install -y redis-server +RUN apt install -y ng-common ng-cjk emacs-nox +RUN apt install -y postgresql postgresql-contrib + +## cuda +RUN apt -y install cuda-drivers +RUN apt -y install libcutensor1 libcutensor-dev libcutensor-doc +RUN wget https://developer.download.nvidia.com/compute/cuquantum/redist/cuquantum/linux-x86_64/cuquantum-linux-x86_64-0.1.0.30-archive.tar.xz +RUN tar xvfJ cuquantum-linux-x86_64-0.1.0.30-archive.tar.xz -C /tmp +RUN cd /tmp/cuquantum-linux-x86_64-0.1.0.30-archive ; tar cf - . | (cd /usr/local; tar vxf -) + +## node.js +RUN apt install -y nodejs npm && npm install n -g && n stable && apt purge -y nodejs npm + +## npm +RUN curl -qL https://www.npmjs.com/install.sh | sh + +## yarn +RUN npm install -g yarn + +## ruby +RUN wget https://cache.ruby-lang.org/pub/ruby/2.7/ruby-2.7.4.tar.gz && tar xvfz ruby-2.7.4.tar.gz && cd ruby-2.7.4 && ./configure && make && make install + +## python3 +RUN apt install -y python3 python3-pip +RUN sudo update-alternatives --install /usr/bin/python python /usr/bin/python3 1 + +ARG DOCKER_UID=1000 +ARG DOCKER_USER=docker +ARG DOCKER_PASSWORD=docker +RUN useradd -u $DOCKER_UID -m $DOCKER_USER --shell /bin/bash && echo "$DOCKER_USER:$DOCKER_PASSWORD" | chpasswd && echo "$DOCKER_USER ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers + +USER ${DOCKER_USER} +RUN echo "\n\ +[user]\n\ + email = ${GIT_EMAIL}\n\ + name = ${GIT_NAME}\n\ +" > /home/$DOCKER_USER/.gitconfig + +SHELL ["/bin/bash", "-l", "-c"] + +# cuQuantum +ENV CUQUANTUM_ROOT=/usr/local +ENV CUSTATEVEC_ROOT=/usr/local +ENV CUTENSOR_ROOT=/usr +ENV CUDA_PATH=/usr/local/cuda +ENV CUQUANTUM_IGNORE_SOLVER=1 +RUN cd /home/$DOCKER_USER && pip3 install cupy-cuda115 numpy scipy cython +RUN cd /home/$DOCKER_USER && git clone https://github.com/NVIDIA/cuQuantum.git && cd cuQuantum/python && pip3 install -v . + +# Qni +RUN cd /home/$DOCKER_USER && echo "cd /home/$DOCKER_USER" >> ~/.bashrc +RUN cd /home/$DOCKER_USER && git clone https://github.com/rbenv/rbenv.git ~/.rbenv +RUN cd /home/$DOCKER_USER && git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build +RUN cd /home/$DOCKER_USER && echo "export PATH=$PATH:$HOME/.rbenv/bin" >> ~/.bashrc +RUN cd /home/$DOCKER_USER && echo "export CUQUANTUM_ROOT=/usr/local" >> ~/.bashrc +RUN cd /home/$DOCKER_USER && echo "export LD_LIBRARY_PATH=${CUQUANTUM_ROOT}/lib64:${LD_LIBRARY_PATH}" >> ~/.bashrc +RUN cd /home/$DOCKER_USER && git clone https://github.com/nakatamaho/qni.git +RUN cd /home/$DOCKER_USER && cd qni && git fetch origin cuquantum-backend +RUN cd /home/$DOCKER_USER && cd qni && git checkout cuquantum-backend + +## settings for rails +RUN cd /home/$DOCKER_USER && cd qni/apps/www && bundle config set path 'vendor/cache' && bundle install && yarn install +RUN cd /home/$DOCKER_USER && cd qni && yarn build && cd apps/www && ./bin/rails css:build && ./bin/rails javascript:build + +## settings for postgresql +RUN sudo -u postgres service postgresql start && sudo -u postgres psql --command "CREATE USER docker WITH SUPERUSER PASSWORD 'docker';" && sudo -u postgres createdb -O docker docker +RUN cd /home/$DOCKER_USER && sudo -u postgres service postgresql start && cd qni/apps/www && ./bin/rails db:create && ./bin/rails db:migrate && ./bin/rails db:fixtures:load + +RUN cd /home/$DOCKER_USER && echo -e "#!/usr/bin/env bash\n\ +export PYTHONIOENCODING=utf-8 \n\ +sudo -u postgres service postgresql start \n\ +cd /home/${DOCKER_USER} ; source ~/.bashrc ; cd qni/apps/www \n\ +./bin/rails s -b 0.0.0.0" > /tmp/startup.sh +RUN chmod 744 /tmp/startup.sh +ENTRYPOINT ["/bin/sh", "-c", "/tmp/startup.sh"] diff --git a/apps/www/Gemfile b/apps/www/Gemfile index 3573fda38..c82f8fdd8 100644 --- a/apps/www/Gemfile +++ b/apps/www/Gemfile @@ -35,6 +35,7 @@ gem 'better_html' gem 'bootsnap', '>= 1.4.2', require: false gem 'grover' gem 'serviceworker-rails' +gem 'pycall' group :development, :test do gem 'byebug', platforms: %i[mri mingw x64_mingw] diff --git a/apps/www/Gemfile.lock b/apps/www/Gemfile.lock index 538263135..1cf9db5dd 100644 --- a/apps/www/Gemfile.lock +++ b/apps/www/Gemfile.lock @@ -165,6 +165,7 @@ GEM public_suffix (4.0.6) puma (5.6.2) nio4r (~> 2.0) + pycall (1.4.1) racc (1.6.0) rack (2.2.3) rack-mini-profiler (2.3.3) @@ -297,6 +298,7 @@ DEPENDENCIES listen (>= 3.0.5, < 3.2) pg puma (~> 5.6) + pycall rack-mini-profiler rails (~> 6.1) rbtrace diff --git a/apps/www/lib/cirq.rb b/apps/www/lib/cirq.rb index ffa7ed05a..a5c8ee961 100644 --- a/apps/www/lib/cirq.rb +++ b/apps/www/lib/cirq.rb @@ -1,3 +1,92 @@ +require 'pycall/import' +include PyCall::Import + +PyCall.exec(<<~PYTHON) +import cirq +import io,sys +import numpy as np +import qsimcirq +from sympy import * +from sympy.parsing.sympy_parser import parse_expr, standard_transformations, implicit_multiplication_application, convert_xor +sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') + +class cirqbridge: + def __init__(self): + return + + def run_simulation(self,numofqubits,_circuit_from_qni): + print("python cirqbridge start") + sys.stdout.flush() + transformations = (standard_transformations + (implicit_multiplication_application,) + (convert_xor,)) + circuit_from_qni = [] + for a in _circuit_from_qni: + if len(a) != 0: + circuit_from_qni.append(a) + print(circuit_from_qni) + sys.stdout.flush() + qubits = cirq.LineQubit.range(numofqubits) + c = cirq.Circuit() + i = 0 + m = 0 + measurement = [] + for column_qni in circuit_from_qni: + print("circuit column", i, column_qni) + i = i + 1 + j = 0 + for circuit_qni in column_qni: + j = j + 1 + print("procssing circit ...") + print(column_qni) + sys.stdout.flush() + if circuit_qni['type'] == u'H': + targetqubits=[ qubits[index] for index in circuit_qni['targets'] ] + if not "controls" in circuit_qni: + c.append([ cirq.H(index) for index in targetqubits] ) + else: + controledqubits=[ qubits[index] for index in circuit_qni['controls'] ] + c.append([ cirq.ControlledOperation(controledqubits, cirq.H(index)) for index in targetqubits ]) + elif circuit_qni['type'] == u'|0>': + targetqubits=[ qubits[index] for index in circuit_qni['targets'] ] + c.append([ cirq.ops.reset(index) for index in targetqubits] ) + elif circuit_qni['type'] == u'|1>': + targetqubits=[ qubits[index] for index in circuit_qni['targets'] ] + c.append([ cirq.ops.reset(index) for index in targetqubits] ) + c.append([ cirq.X(index) for index in targetqubits] ) + elif circuit_qni['type'] == u'': + pass #nop + else: + print("unsupported gate", circuit_qni['type']) + print("circuit column", column_qni) + + print("") + print('Cirq circiut') + print(c) + cirq_simulator = cirq.Simulator() + cirq_result = cirq_simulator.simulate(c) + print('Cirq result:') + print(cirq_result) + sys.stdout.flush() + + qsim_simulator = qsimcirq.QSimSimulator() + qsim_result = qsim_simulator.simulate(c) + print('qsim result:') + print(qsim_result) + sys.stdout.flush() + + gpu_options = qsimcirq.QSimOptions(use_gpu=True) + qsim_simulator = qsimcirq.QSimSimulator(qsim_options=gpu_options) + qsim_gpu_result = qsim_simulator.simulate(c) + print('qsim GPU result:') + print(qsim_gpu_result) + sys.stdout.flush() + print("") + + print("python cirqbridge end") + sys.stdout.flush() + #return (result.final_state_vector) + +PYTHON + class Cirq def initialize(circuit_id:, qubit_count:, step_index:, steps:, targets:) @circuit_id = circuit_id @@ -8,6 +97,8 @@ def initialize(circuit_id:, qubit_count:, step_index:, steps:, targets:) end def run + cirqbridge = PyCall.eval('cirqbridge').call + cirqbridge.run_simulation(@qubit_count, @steps) @steps.map.with_index { |each, index| execute_step each, index } end @@ -31,8 +122,8 @@ def execute_step(step, index) # {"type"=>"Z", "controls"=>[], "if"=>nil} when 'P' # {"type"=>"P", "phi"=>"pi/2", "controls"=>[], "targets"=>[0], "if"=>nil} - when 'X^½' - # {"type"=>"X^½", "controls"=>[], "if"=>nil} + when 'X^' + # {"type"=>"X^", "controls"=>[], "if"=>nil} when 'Rx' # {"type"=>"Rx", "theta"=>"pi/2", "controls"=>[], "targets"=>[], "if"=>nil} when 'Ry' @@ -41,8 +132,8 @@ def execute_step(step, index) # {"type"=>"Rz", "theta"=>"pi/2", "controls"=>[], "targets"=>[], "if"=>nil} when 'Swap' # {"type"=>"Swap", "controls"=>[], "targets"=>[]} - when '•' - # {"type"=>"•", "targets"=>[0]} + when '' + # {"type"=>"", "targets"=>[0]} when 'Bloch' # {"type"=>"Bloch"} when 'Write' @@ -50,7 +141,9 @@ def execute_step(step, index) when 'Measure' # {"type"=>"Measure", "flag"=>""} measured_bits[bit] = [0, 1].sample # rubocop:disable Performance/CollectionLiteralInLoop - when '1' + when '|0>' + # NOP + when '|1>' # NOP else raise "Unknown operation: #{each.inspect}"