いざどりのtrial and error

人生は試行錯誤の連続。

qpdfviewでパスワード付きPDFが印刷できない

Lubuntu Linuxでパスワード付きのPDFを印刷しようとしたところ、エラーが出て印刷できなかった。ので、忘れないようにメモ。

環境

  • Lubuntu Linux 20.04LTS
  • CUPS 2.3.1
  • qpdfview 0.4.18-1(Lubuntuでは標準)

現象

qpdfviewでパスワード付きPDFを開いて、普通に印刷したところ、次のようなエラーが出た。

f:id:izadori:20210217153322p:plain

ウィンドウのメッセージからは何が起こっているのか全くわからなかったので、CUPSを開いて見てみたところ、こんなメッセージが表示されていた。

Wed Feb 17 xx:xx:xx 2021
から処理中
"loadfilename failed: /var/spool/cups/d00002-001: invalid password"

パスワードが無効?? なんで??

CUPSの設定でなにか間違えたのかと思って、直接CUPSからテストページの印刷を行ってみたところ、問題なく印刷される。

対処方法

原因がわからず困ったので、検索をかけたところ、次のような情報を見つけた。

Bug #1476693 “unable to print password protected PDF documents” : Bugs : qpdfview

これによると、qpdfviewの問題のようだ。確かに、evinceでは同じファイルが問題なく印刷できた。

ということで、qpdfviewではパスワード付きPDFファイルの印刷に対応できないので、他のPDFビューアを使うのが吉。

・・・それにしてもこの現象、2015年には報告されているのね。

【C++】【Eigen】MATLABのdiff()関数(っぽいもの)を実装する

先日C++でプログラムを作成していたところ、MATLABdiff()のようなものが必要になったので、Eigenを使って同様の関数を実装してみた。

更新履歴

  • 21.02.15 Eigenによる実装部分の文章を加筆・修正。合わせてC++ソースコードも修正。
  • 21.02.09 初稿

MATLABのdiff()関数とは

MATLABdiff()関数は隣り合う要素の差分を返す関数。
行列に対しては、行間の差分を取る。m行n列の行列に対してdiff()を適用すると(m-1)行n列の行列が返される。
(列間の差分も取れるが)

また、diff(X, k)のように書くことで再帰的に複数回適用できる。

Y = diff(eye(5), 2)

1 -2  1  0  0
0  1 -2  0  0
0  0  1 -2  1

似たような関数はpythonのnumpyやRにもある。pythondiff()ではMATLABと動作が違い、隣り合う列の差を取ったものを返す。
一方、Rのdiff()MATLABと同じで隣り合う行の差を取ったものを返すようだ。いずれも再帰的に複数回適用することも可能。

Eigenによる実装

Eigenを使い、C++でこれと似たような関数を実装する。

Eigenは式テンプレートという仕組みを使用していることから、Matrixクラスのオブジェクトを引数に取る関数を使う場合は、Matrix<...> &とするよりは、MatrixBase<Derived> &として、テンプレート関数にしたほうがよい。このようにすると、Matrix<>::Identity()や行列を演算したものなどを直接引数に取れる。

When writing a function taking Eigen objects as argument, if you want your function to take as argument any matrix, vector, or expression, just let it take a MatrixBase argument.

(訳)Eigenオブジェクトを引数として取る関数を作成するときに、関数に任意の行列、ベクトル、または式を引数として使用させたい場合は、MatrixBase引数をとるようにする。
MatrixBaseクラスのリファレンスより)

実装にあたり、EigenのMatrixクラスには、行列の一部を取り出して部分行列を作るメソッドがあるので、これを使う。

初稿では、下記のように.topRows().bottomRows()を使うバージョンだったが、forループの中で.eval()が必要であり、毎回オブジェクトの生成とコピーが発生していたことから、効率が良くなかった(と思われる)。また、これだとVectorXdなども引数に取れるが、戻り値がMatrixXdになってしまう。

改訂版では、これを修正することにする。ついでにMatrixXiなどMatrixXd以外にも対応し、列間の差分も取れるようにした。

初稿版

#include <Eigen/Dense>

template <typename Derived>
const Eigen::MatrixXd Diff(const Eigen::MatrixBase<Derived> & m, const unsigned int n = 1)
{
    Eigen::MatrixXd mr = m;

    for(unsigned int i = 0; i < n; i++)
    {
        mr = (mr.topRows(mr.rows() - 1) - mr.bottomRows(mr.rows() - 1)).eval();
    }

    return mr;
}

改訂版

実装方針として、密行列に対しては部分行列を作るメソッドは左辺値にも使うことができるので、部分行列で計算した結果を元の行列に戻すことで、オブジェクトの生成とコピーの発生を抑制する。

行間の差分を取る際に.topRows()はそのまま使うが、.bottomRows()ではなく、.block()で取り出す行数を変えるようにする。列間の差分を取る場合は、同様に.leftCols().block()を使用する。

なお、MatrixXd以外に対応させるために、Derived::PlainObjectを使う。

template <typename Derived>
const typename Derived::PlainObject
Diff(const Eigen::MatrixBase<Derived> & m0, const int n = 1, const int dim = 0)
{
    typename Derived::PlainObject m = m0;
    int rows = m.rows(), columns = m.cols();

    // dim == 1で列間の差分を取る
    if(dim == 1)
    {
        for(int i = 0; i < n ; i++)
        {
            int col_counts = columns - i - 1;
            m.leftCols(col_counts) = m.leftCols(col_counts) - m.block(0, 1, rows, col_counts);
        }

        return m.leftCols(columns - n);
    }
    else
    {
        for(int i = 0; i < n ; i++)
        {
            int row_counts = rows - i - 1;
            m.topRows(row_counts) = m.topRows(row_counts) - m.block(1, 0, row_counts, columns);
        }

        return m.topRows(rows - n);
    }
}

ところで、残念なことにSparseMatrixに対しては左辺値に使えない様子なので、SparseMatrix版の実装は初稿版と同じ方針を取ることにした。
(なにか良い方法はないものか)

SparseMatrix版まで実装したソースコードdiff.hとしてGitHubに公開した。Eigenと同じようにヘッダファイルのインクルードのみで動作する。

使用例

#include <iostream>
#include "diff.h"

int main()
{
    Eigen::MatrixXf mf(4, 4);
    Eigen::MatrixXi mi = Eigen::MatrixXi::Identity(5, 5);
    Eigen::VectorXd vd(5);
    Eigen::RowVectorXd rvd(5);
    Eigen::SparseMatrix<double> sm;

    mf  << -2,  1,  0,  3,
            3,  2, -2,  3,
            5,  0, -1, -3,
            1, -1,  4,  1;

    vd  << 0, 3, 2, 2, 4;
    rvd << 5, 3, 2, 0, 1;

    std::cout << "Diff(Eigen::MatrixXd::Identity(5, 5), 2) = " << std::endl;
    std::cout << Diff(Eigen::MatrixXd::Identity(5, 5), 2) << std::endl << std::endl;

    std::cout << "Diff(mf, 2, 1) = " << std::endl;
    std::cout << Diff(mf, 2, 1) << std::endl << std::endl;

    std::cout << "Diff(mi - Eigen::MatrixXi::Ones(5, 5), 3) = " << std::endl;
    std::cout << Diff(mi - Eigen::MatrixXi::Ones(5, 5), 3) << std::endl << std::endl;

    std::cout << "Diff(vd) = " << std::endl;
    std::cout << Diff(vd) << std::endl << std::endl;

    std::cout << "Diff(rvd, 1, 1) = " << std::endl;
    std::cout << Diff(rvd, 1, 1) << std::endl << std::endl;

    sm = Eigen::VectorXd::Ones(5).asDiagonal();
    std::cout << "Diff(sm, 2) = " << std::endl;
    std::cout << Diff(sm, 2) << std::endl;

    return 0;
}

出力結果

Diff(Eigen::MatrixXd::Identity(5, 5), 2) =
 1 -2  1  0  0
 0  1 -2  1  0
 0  0  1 -2  1

Diff(mf, 2, 1) =
-4  4
-3  9
 4 -1
 7 -8

Diff(mi - Eigen::MatrixXi::Ones(5, 5), 3) =
 1 -3  3 -1  0
 0  1 -3  3 -1

Diff(vd) =
-3
 1
 0
-2

Diff(rvd, 1, 1) =
 2  1  2 -1

Diff(sm, 2) =
Nonzero entries:
(1,0) (-2,0) (1,1) (1,0) (-2,1) (1,2) (1,1) (-2,2) (1,2)

Outer pointers:
0 1 3 6 8  $

1 -2 1 0 0
0 1 -2 1 0
0 0 1 -2 1

動作を確認した環境

【python】Lubuntu Linuxでpipを使えるようにする

はじめに

Lubuntu Linux 20.04 LTSでmatplotlibやnumpyをインストールしようとして、 pipと打ち込んだところ

$ pip install matplotlib numpy
pip: コマンドが見つかりません。

と出て使えなかった。ので、使えるようにするまでの手順をメモ。

手順

  1. get-pip.pyの取得
  2. python3-distutilsパッケージのインストール
  3. get-pip.pyでpipをインストール

get-pip.pyの取得

wgetcurlを使用して取得する。

wget http://bootstrap.pypa.io/get-pip.py

検索して得られる情報だと、このままpython get-pip.pyとすればいいように思われるが、Lubuntu Linuxでは、

$ sudo python get-pip.py
Traceback (most recent call last):
  File "get-pip.py", line 24244, in <module>
    main()
  File "get-pip.py", line 199, in main
    bootstrap(tmpdir=tmpdir)
  File "get-pip.py", line 82, in bootstrap
    from pip._internal.cli.main import main as pip_entry_point
  File "<frozen zipimport>", line 259, in load_module
  File "/tmp/tmpk2i63bp3/pip.zip/pip/_internal/cli/main.py", line 8, in <module>
  File "<frozen zipimport>", line 259, in load_module
  File "/tmp/tmpk2i63bp3/pip.zip/pip/_internal/cli/autocompletion.py", line 9, in <module>
  File "<frozen zipimport>", line 259, in load_module
  File "/tmp/tmpk2i63bp3/pip.zip/pip/_internal/cli/main_parser.py", line 7, in <module>
  File "<frozen zipimport>", line 259, in load_module
  File "/tmp/tmpk2i63bp3/pip.zip/pip/_internal/cli/cmdoptions.py", line 20, in <module>
  File "<frozen zipimport>", line 259, in load_module
  File "/tmp/tmpk2i63bp3/pip.zip/pip/_vendor/packaging/utils.py", line 9, in <module>
  File "<frozen zipimport>", line 259, in load_module
  File "/tmp/tmpk2i63bp3/pip.zip/pip/_vendor/packaging/tags.py", line 7, in <module>
ModuleNotFoundError: No module named 'distutils.util'

と出て、インストールできない。エラーメッセージを見ると、distutils.utilが不足しているようだ。
そこでpython3-distutilsもインストールしておく。

ちなみにLubuntu Linux 20.04 LTSでは、単にpythonとすると、python 2.xが起動するので、python 3.xを使う場合は前もって、

sudo update-alternatives --install /usr/bin/python python /usr/bin/python3.x 2

として、pythonpython 3.xが起動するように割り当てておく方がよいかも。
割り当てない場合はpythonpython3に置き換える。

(参考) Ubuntu20.04のデフォルトで利用されるpythonコマンドのバージョンを3系に変更する - Qiita

python3-distutilsパッケージをインストール

単にMuonパッケージマネージャーでdistutilsで検索してインストールしてもよいし、

sudo apt install python3-distutils

でもよい。

get-pip.pyでpipをインストール

python3-distutilsをインストールしたら再度

sudo python get-pip.py

を実行してpipをインストールする。

$ python -m pip -V
pip 21.0.1 from /usr/local/lib/python3.8/dist-packages/pip (python 3.8)

これでようやくpythonのモジュールがインストールできるようになる。

VirtualBox内のLinuxでVSCodeの画面のちらつきを回避する

概要

VirtualBoxのゲストOSとしてLubuntu Linuxをインストールした環境に、Visual Studio Codeをインストールしたら、 Visual Studio Codeの画面がやたらとちらつく現象に出くわしたので、回避方法のメモ。

環境

  1. ホストOS
  2. VirtualBox
    • version 6.1.16 r 140961
  3. ゲストOS
    • Lubuntu Linux 20.04 LTS + VirtualBox Guest Additions CD
    • kernel 5.4.0-65-generic
  4. Visual Studio Code
    • version 1.52.1

現象と回避方法

画面がちらつくと書いたが、ちらつくほかにも、ウィンドウの一部が表示されなかったり、閉じたウィンドウの文字が残ったりすることがある。
Googleで検索したら次のページが見つかった。

qiita.com

このページによると、code --disable-gpuGPUによる描画を無効にするオプションを付けて起動すればよいらしい。
試してみたところ、確かに画面のちらつきがなくなった。

・・・と、調べたところで、VirtualBoxのディスプレイ設定にある「3Dアクセラレーションを有効化」オプションにチェックを入れていたことに気が付いた。
このオプションは初期設定ではOFFになっているものだが、元々LubuntuではなくUbuntu LinuxをゲストOSにしようとして有効にしたものだった。

f:id:izadori:20210202235034p:plain

このチェックを外したら、--disable-gpuオプションを付けなくても画面のちらつきがなくなった。

仮想環境に3D描画性能を求めないのであれば、こちらの方が簡単でよいかも。

Gitの使い方メモ

Gitを使い始めてまだなれていないため、自分なりに調べた使い方のメモ。間違い・勘違いしているところも多々あるはず。

21.02.16 更新

環境

Windows 10

> git --version
git version 2.30.0.windows.1

Lubuntu Linux 20.04 LTS

$ git --version
git version 2.25.1

ローカルリポジトリの作成

/path/to/repository以下にtestというリポジトリを作成する場合

cd /path/to/repository
mkdir test
cd test
git init

このようにすることで/path/to/repository/test/以下にローカルリポジトリが作成される。

ローカルリポジトリへのファイルの追加

testディレクトリ内に何かファイルを作成後、作成したファイルをインデックスに登録する。(例)README.md

 echo "# test" > README.md
 git add README.md

複数のファイルを登録する場合は、git add -Agit add .git add -uでも良い。違いは次の通り。

  1. git add -Aは新規作成・更新・削除のあったリポジトリ内のすべてのファイルを追加する。
  2. git add .はカレントディレクトリ以下で新規作成・更新・削除のあったファイルを追加する。
  3. git add -uは更新・削除のあったリポジトリ内のすべてのファイルを追加する。新規作成されたファイルは対象外。

なお、.gitignoreに記載されたファイルはGitの管理対象から除外されるので、そのようなファイルがある場合は作成しておくこと。

また、空のディレクトリはGitの管理対象外。空のディレクトリを保持しておきたい場合は、.gitkeepといった中身が空のファイルや、次のような内容を記載した.gitignoreを置いておくと良い。

*
!.gitignore

.gitignore.gitkeepについては、このあたりが参考になる。

インデックスに登録したら、次のコマンドでコミットする。

git commit -m "commit message"

-mオプションは複数つけることも可能。この場合、個別の段落として扱われる。また、コミットメッセージをファイルに記録しておいて、-Fオプションでファイル名を指定することも可能。

git commit -F file

GitHubにリモートリポジトリを作成する

  1. GitHubにログインする。
  2. 右上の”+”をクリックし、"New repository"を選択する。 f:id:izadori:20210125215825p:plain
  3. リポジトリ名を入力する。
  4. リポジトリを"Public"と"Private"のどちらにするか選択する。無料プランだと"Public"のみが指定できる。
  5. リポジトリをどのように初期化するか、必要とするものにチェックを入れる。ローカルリポジトリの内容をリモートリポジトリに反映させる場合は、いずれにもチェックを入れない。
  6. ”Create repository"を押してリポジトリを作成する。 f:id:izadori:20210125215829p:plain

このように作成したリモートリポジトリをローカルリポジトリに登録するにはローカルでgit remote addを使用する。

例えばリポジトリtestをユーザー名userが作成した場合は次のようにする。

git remote add origin https://github.com/user/test.git
  • SSH接続の場合
git remote add origin git@github.com:user/test.git

git remote -vで現在登録されているリモートリポジトリが確認できる。

$ git remote -v
origin  https://github.com/user/test.git (fetch)
origin  https://github.com/user/test.git (push)

コミットしたローカルリポジトリの内容をリモートリポジトリへ反映させる

ローカルリポジトリの内容をリモートリポジトリに反映させるためにはgit pushを使用する。

git push origin master

これによりローカルリポジトリmasterブランチの内容がリモートリポジトリoriginmasterブランチに反映される。

git push -u origin masterとした場合、origin上のmasterブランチがローカルのmasterブランチの上流ブランチとして設定される。

「上流ブランチ」とは、あるローカルブランチが更新を追跡するリモートブランチのことらしいが、まだ良く理解できていない。

上流ブランチを設定すると引数なしのgit pushでリモートブランチを最新の情報に更新したり、git pullで最新のリモートブランチの情報を取ってくることができる。

リモートリポジトリの内容を新たにローカルに複製する

ローカルにリポジトリを複製するには、git cloneを使用する。

GitHubを例に取り、GitHub上のユーザー名userリポジトリtestをローカルに複製するには次のようにする。

git clone https://github.com/user/test.git
  • SSH接続の場合
git clone git@github.com:user/test.git

Windowsでclone時に「証明書が見つからない」エラーが出る場合(21.02.16追加)

Windowsgit cloneしようとしたときに証明書が見つからないと言われることがある。

> git clone https://github.com/user/test.git
Cloning into 'test'...
fatal: unable to access 'https://github.com/user/test.git/': error setting certificate verify locations:  CAfile: C:/Program Files/Git/mingw64/ssl/certs/ca-bundle.crt CApath: none

この場合、次のように対処する。

  1. Git Bashを立ち上げる。
    f:id:izadori:20210216133810p:plain
  2. /etc/gitconfigを開く
  3. sslCAInfo行のパスを正しいものに直す。 f:id:izadori:20210216133835p:plain

リモートリポジトリの内容をローカルリポジトリへ取り込む

リモートリポジトリの内容をローカルリポジトリに取り込むためにはgit pullを使用する。

git pull origin master

これによりリモートリポジトリのmasterブランチの内容がローカルリポジトリmasterブランチに取り込まれる。

ベアリポジトリの作成

ベアリポジトリとは、作業用のファイル持たず、管理用のファイルのみを持つリポジトリのこと。/path/to/repository以下にベアリポジトリbare.gitを新規作成する場合は、次のようにする。

cd /path/to/repository
mkdir bare.git
cd bare.git
git init --bare

ベアリポジトリディレクトリは、慣例としてこの例のように.gitで終わる名前にするようだ。

また、既存のリポジトリ/path/to/srcからカレントディレクトリにベアリポジトリbare.gitを作成する場合は、次のようにする。

git clone --bare /path/to/src bare.git

コミットした内容を取り消す

誤ってコミットしてしまったものを取り消す方法は2種類。

  • git reset
  • git revert

git resetは前のコミットした状態まで戻すときに使う。このとき過去の履歴も消える。直前のコミットだけを取り消すときは、

git reset --soft HEAD^

インデックスに登録した内容まで取り消すときには、

git reset --hard HEAD^

とする(ファイルも変更内容が取り消される)。誤ってresetしてしまった場合は、HEAD^の代わりにORIG_HEADを指定することで元に戻せる。

git resetについてはこのあたりが参考になる。

一方、git revertは、前のコミットを打ち消すときに使う。git resetと違い、「コミットを打ち消した」ことをコミットするため、過去の履歴は残る。

直前のコミットを打ち消す場合は、次のようにする。

git revert HEAD

ブランチの作成と切り替え(21.02.16追加)

新たなブランチ(例:anotherbranch)を作成する場合は、git branchを使う。また、ブランチを切り替えるには、git checkoutを使う。

git branch anotherbranch
git checkout anotherbranch

次のようにすることで、ブランチの作成と切り替えを一度に行うことができる。

git checkout -b anotherbranch

なお、ブランチの切り替えを行う際に、コミットされていない変更がある場合は、切り替えできないので、事前にコミットしておく。
コミットしたくない場合は、git stashを使う。

ブランチのマージ(21.02.16追加)

anotherbranchブランチで行った変更をmasterブランチにマージする場合は、一旦マージ先のmasterにブランチを切り替えてからマージを実行する。

git checkout master
git merge master anotherbranch

ブランチの削除(21.02.16追加)

不要になったブランチ(例:anotherbranch)を削除する場合は、次のようにする。

git branch -d anotherbranch

(参考:Git公式ドキュメント)