BFG批量修改git历史

git使用时经常会不慎提交一下不该提交的东西,比如把build文件夹整个提交了,或者把密码提交了。此时可以使用工具批量修改历史,再git push -f更改历史记录。

根据GitHub文档的建议,我们可以使用BFG来清理,如

1
2
3
4
cp repo-to-clean repo-to-clean2
cd repo-to-clean2
bfg --strip-blobs-bigger-than 100M --replace-text patterns.txt --delete-files YOUR-FILE-WITH-SENSITIVE-DATA
git push -f

中文

BFG并不直接支持utf,而是支持根据文件的头几个字母判断编码,需要设置变量file.encoding=UTF8提示编码。方法包括

  • 设置环境变量JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF8
  • 运行时使用java -D"file.encoding=UTF8" -jar bfg ...参数...直接定义变量

CMake入门

CMake是一个C语言的项目管理器,用于简便地指明一个C项目生成应用/库的方法,并生成编译脚本(如Makefile)或IDE项目文件(vs解决方案sln),用于最终编译C文件。
相比于其他的的项目管理器,CMake的特点是:

  • 懂C。相比于通用的make,CMake的项目组织方法和C的特性息息相关,并且内置了众多常见的编译选项,可以避免记忆常见的命令参数。
  • 通用。一方面大量项目使用CMake管理,CMake众多C工具中的最大公约数,可以用于生成VS的项目,也可以支持VSCode,也可以在*nix环境中适应gcc或clang。

C项目

要理解CMake,首先要知道C项目指的是什么。C项目包括源代码(源文件.c/.cpp和头文件.h)以及对应的编译选项,生成若干个目标(target)。对象指的是可直接执行的应用,或可以被用于构建新目标的库。

  • 头文件
  • 源文件
  • 目标(二进制文件)
  • 编译选项

应用依赖只有环境和动态库,但库还包括了依赖的库、头文件、编译选项

两个抽象层:命令vs函数

命令和变量

由于语言的特点,C项目不只包括代码,还需要包括能够编译代码的编译命令,这些命令不只用于找到对应的代码,还需要调整一些功能的开关,如RTTI。

add_custom_target 示例

变量

CMAKE_CXX_FLAGS

使得多个命令共享参数,但由于是全局的,可能导致冲突

/utf-8

generator expression

函数和属性

target_compile_options

可以灵活地设定全局或者只针对若干目标指定,可以避免副作用互相影响。

属性

set_property get_property

编译模型

自己的库

只介绍最现代的“面向目标”的方法,add_include_directories、include_directories等不介绍。

add_executable/add_library

生成目标
“构造函数”

静态/动态库

不需要指定具体命令
INTERAFACE见下

target_include_directories

指定头文件(目录)
具体需要什么头文件在c代码中include

指定库,包括库的组成部分头文件、二进制文件、编译选项

target_compile_options

指定编译选项

INTERFACE/PRIVATE/PUBLIC

他有、私有、他有及私有

INTERFACE库

不生成文件,用于指定头文件目录和编译选项。添加的文件仅用于在ide中展示

别人的库

find_package

获得上述目标

FetchContent

可以下东西,但是目前还没解决

利用cmake编译cuda程序

CUDA教程中的编译方法通常是直接调用nvcc(windows需在x64 Native Tools Command Prompt中运行)或者在vs中CUDA集成,前者不太方便自动控制,后者比较麻烦,我尝试使用cmake生成。

CUDA编译的东西很多,这里只编译一个独立可执行文件。

VS集成

Building issues [no CUDA toolset found] · Issue #103 · mitsuba-renderer/mitsuba2 · GitHub

VS2019需要CUDA 10.1以上,另外在我的电脑上出现了能找到nvcc却找不到CUDA的问题,解决方案如上。

CUDA样例

来自An Even Easier Introduction to CUDA | NVIDIA Developer Blog。命名为main.cu

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <math.h>

#include <iostream>
// Kernel function to add the elements of two arrays
__global__ void add(int n, float *x, float *y) {
for (int i = 0; i < n; i++) y[i] = x[i] + y[i];
}

int main(void) {
int N = 1 << 20;
float *x, *y;

// Allocate Unified Memory – accessible from CPU or GPU
cudaMallocManaged(&x, N * sizeof(float));
cudaMallocManaged(&y, N * sizeof(float));

// initialize x and y arrays on the host
for (int i = 0; i < N; i++) {
x[i] = 1.0f;
y[i] = 2.0f;
}

// Run kernel on 1M elements on the GPU
add<<<1, 1>>>(N, x, y);

// Wait for GPU to finish before accessing on host
cudaDeviceSynchronize();

// Check for errors (all values should be 3.0f)
float maxError = 0.0f;
for (int i = 0; i < N; i++) maxError = fmax(maxError, fabs(y[i] - 3.0f));
std::cout << "Max error: " << maxError << std::endl;

// Free memory
cudaFree(x);
cudaFree(y);

return 0;
}

CMake模板

来自Building Cross-Platform CUDA Applications with CMake | NVIDIA Developer Blog。需要编译动态库的话见原文。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
cmake_minimum_required(VERSION 3.12 FATAL_ERROR)
project(cmake_and_cuda LANGUAGES CXX CUDA)

# INTERFACE库enable_utf8用于在msvc中启用utf-8
add_library(enable_utf8 INTERFACE)
if(MSVC)
# cmake的generator expression可以根据语言选择参数,编译c++时使用前者,CUDA使用后者
target_compile_options(enable_utf8 INTERFACE
$<$<BUILD_INTERFACE:$<COMPILE_LANGUAGE:CXX>>: /utf-8 >
$<$<BUILD_INTERFACE:$<COMPILE_LANGUAGE:CUDA>>: -Xcompiler=/utf-8 >
)
endif()

add_executable(test main.cu)

# 连接到INTERFACE库enable_utf8即可启用utf-8
target_link_libraries(test PRIVATE enable_utf8)
# 注:INTERFACE表示被连接到的目标使用,PRIVATE表示库自己使用,PUBLIC为前两者并集,即自己和被连接到的项目都可以。

基本上就是语言里加个CUDA就解决了。如果用VS,需要借助nvcc向msvc传选项/utf-8来避免编码问题,添加的方法来自passing flags to nvcc via CMake - #2,来自 qiyupei - CUDA Programming and Performance - NVIDIA Developer Forums。(居然是OSPP项目导师,巧了)

另有FindCUDAToolkit或者FindCUDA方法,不太清楚。

运行

可执行文件可以直接运行,如果需要profile可以nvprof 文件名

julia画图拟合

场景

用Julia画图时经常会遇到偏差比较大,或者需要外推的数据,如:

1
2
3
4
data = [
1 2 3 4
1 4-1 9+1 16
]

这时候就只能先针对现有的数据拟合一个函数再画。

GLM.jl

来源:Examples · GLM

一种方法是用回归的方法拟合出模型再画图。回归的流程是先将数据装入DataFrame,给出结果后拟合。

1
2
3
4
df = DataFrame(X=data[1,:], Y=vec(data[2,:]))
ols = lm(@formula(Y ~ X), data)
x1 = 0:.1:5
y1 = predict(ols, DataFrame(X=x1))

vec用来转换类型,这里不用也行。

最后用plot!(x1, y1)画图就好了。

Loess.jl

来源:GitHub - JuliaStats/Loess.jl: Local regression, so smooooth!

如果不能用简单公式拟合,可以尝试用更加通用的Loess。但Loess的缺点是不能外推。

1
2
3
model = loess(data[1,:], data[2,:], span=.7)
x2 = range(extrema(data[1,:])...; step = 0.1)
y2 = Loess.predict(model, x2)

完整示例

glm-and-loess.jl

PyTest及CI模板

pytest模板

1
2
3
4
5
6
7
project/
module/
__init__.py - 空
main.py
tests/
test_XXX.py
conftest.py - 空

其中main.py为普通的python模组代码,test_XXX.py内容为:

1
2
3
4
import module.main

def test_xxx1():
assert module.main.xxx() == 1

使用指令pytest即可运行测试,pytest会自动将conftest.py所在路径加入搜索路径。

test fixture

fixture是测试的资源,在测试函数中名字为某一fixture的参数会被自动填上对应的函数的返回值。

1
2
3
4
5
6
7
8
import pytest

@pytest.fixture
def one():
return 1

def test_one(one);
assert one == 1

GitHub Actions模板

此模板要求使用pipenv来管理包。创建文件.github/workflows/app.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
name: PyTest
on: push

jobs:
test:
runs-on: windows-latest
timeout-minutes: 10

steps:
- name: Check out repository code
uses: actions/checkout@v2

# Setup Python (faster than using Python container)
- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: "3.x"

- name: Install pipenv
run: |
python -m pip install --upgrade pipenv wheel
- id: cache-pipenv
uses: actions/cache@v1
with:
path: ~/.local/share/virtualenvs
key: ${{ runner.os }}-pipenv-${{ hashFiles('**/Pipfile.lock') }}

- name: Install dependencies
if: steps.cache-pipenv.outputs.cache-hit != 'true'
run: |
pipenv install --deploy --dev
- name: Run test suite
run: |
pipenv run test -v

对应的徽章是

1
[![CI](https://github.com/用户名/仓库/actions/workflows/app.yaml/badge.svg)](https://github.com/用户名/仓库/actions/workflows/app.yaml/)

VSCode

参见:https://code.visualstudio.com/docs/python/testing

coverage

总之就是想办法添加pytest-cov库,然后运行pytest --cov=目录 --cov-report=xml然后上传,可以直接在上面的app.yaml后面加上

1
2
3
4
5
6
7
8
9
- name: Generate coverage report
run: |
pipenv run pytest --cov=./ --cov-report=xml

- name: "Upload coverage to Codecov"
uses: codecov/codecov-action@v2
with:
fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos

目录是./的时候似乎测试文件也会被统计到,或许改成具体的项目会更好。

GoogleTest模版CMakeLists

  • 项目是动态库MyMatrix
  • 在windows下自动导出所有符号,使用utf-8编码
  • 使用GitHub反代fastgit.org
  • 自动下载GoogleTest,不需要额外安装
  • 自动设置vs启动项
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# https://google.github.io/googletest/quickstart-cmake.html#create-and-run-a-binary
cmake_minimum_required(VERSION 3.14)
project(MyMatrix)

if(MSVC)
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
# 更优雅的方法参见CUDA那篇
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /utf-8")
endif()

set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS true)
add_library(MyMatrix SHARED MyMatrix.cpp)

option(BUILD_GMOCK OFF)
include(FetchContent)
FetchContent_Declare(
googletest
# Specify the commit you depend on and update it regularly.
URL https://hub.fastgit.org/google/googletest/archive/refs/tags/release-1.11.0.zip
)
# For Windows: Prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)

add_executable(tests MyMatrix-tests.cpp)
target_link_libraries(tests gtest_main)
enable_testing()
add_test(NAME tests COMMAND tests)
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT tests)

常见cmake指令:

  • 生成:cmake -B build
  • 编译:cmake --build build
  • 打开(vs解决方案):cmake --open build

MyMatrix-tests.cpp内容参见Quickstart: Building with CMake | GoogleTest

GitHub镜像站hub.fastgit

记录一下,非常快的GitHub镜像站fastgit.org。根据需要,把github链接中的网站替换成对应的代理即可。如

1
git clone https://github.com/JuliaInterop/Clang.jl.git

改为

1
git clone https://hub.fastgit.org/JuliaInterop/Clang.jl.git

就可以极速克隆。

人类征服野生线性微分方程组实录

前言

某校的拉普拉斯变换主要的考试方法是解线性微分方程(组),解这样的方程组的过程是

  1. 根据初值条件,把给定的微分方程左边和右边都转换到s域
  2. 做除法或者消元,算得$Y=\mathcal L \left(y\right)$在s域的表达式
  3. 进行部分因式分解
  4. 把结果转换回时域

其中第一和第四步都是拉普拉斯变换的内容,在上篇文章已经详细说明。而解方程组和因式分解……懂的都懂。不过根据某数学菜鸡的经验,拉普拉斯变换的部分花的时间少但容易错,因式分解的部分虽然计算艰难但可以验算,复变内容背得熟练的话基本上不会出错。

为避免反复抄几个字母无数次展现本人仅有的线性代数水平,本文中所有的线性方程组都用矩阵形式表示。

二次部分分式分解

矩阵……真的存在吗

分母因式分解后,最容易解决的是实数线性项(的最高次项),利用“遮盖法”即可快速算出结果。

而二次项有两个办法,一个是解出对应的两个虚根,算出结果后再合并;另一个办法便是解方程组了。

在没有计算器的情况下,复数乘除法非常难算,且似乎不易优化。而方程组的方法则比较整齐,适合强迫症,且比较容易验算。

例:

A和B很容易求出

而C和D列方程组求解

A B C D
$s^3$ 1 1 1 0 0
$s^2$ 3 1 4 1 0
$s$ 4 4 3 4 130
1 12 4 0 3 0

左边两列有很明显的周期性,若把第一行的-4倍加到第三行,第二行的-4倍加到第四行,省略前两行

直接就可以算出C和D。这是$s^2+a^2$类型的简单之处。若是之前没算出A和B,这里也可以带入再计算。

如果有时间验算,除了可以计算这个矩阵方程本身,还可以带入s为特定值直接检验分式分解的正确性。

解符号表达式方程组

某人认为解二元带符号的方程组,最好的办法是直接背诵

比如解

先求出

再左乘右边的向量就好了。这样不需要反复地抄写和化简符号表达式,一直都是最简的。

拉普拉斯变换应试指南

前言

复变上半学期学了解析函数相关一大堆,下半学期的内容只安排了傅立叶变换和拉普拉斯变换,其中傅立叶变换只有两填空和一证明,而拉普拉斯变换有三小计算两大计算,基本上统揽了试卷的所有计算量。

本文梳理了崔老师复变在习题和考试中大部分的技能点,只讲应试,不讲理解。

技能树·正反拉普拉斯变换

拉普拉斯变换列出来的式子有二十个左右,但是由于每个都是很细的知识点,除了基本拉普拉斯变换个别可能不考以外,其他全部都考到,只是题型的区别。

出生技能·基本拉普拉斯变换

每题都涉及,不然考啥呢

以下默认时域变量为$t$,s域变量为$s$。

多项式的拉普拉斯变换

注意转换到s域之后次数多一。

(双曲)三角函数的拉普拉斯变换

注意cos是sin的导数,所以上面多一个s。三角是加号,而双曲是减号,是因为$s^2+a^2=0$的两个根都是虚根,而$s^2-a^2=0$的两个根都是实根。逆变换的时候有些技巧,见进阶I·延时和频移反向变换部分。

进阶I·延时和频移

大计算必考,小计算好像还真没考

两个实际上是对偶的,指数和平移互相变换。注意延时的时候是把$f(t)$的图像整个平移,不是$f(t)\cdot u(t-a)$,后面具体说明

其中$u$是阶跃函数。

正向变换

不管是计算哪个方向的变换,先计算平移的特性,再计算指数项($e^{at}$和$e^{-as}$)。

正向时,先考虑延时,做对应的基本变换,再考虑指数项。如计算下列分段函数的拉普拉斯变换:

(1)把函数写成阶跃的形式

把每段写成阶跃的形式,把$(a,b)$范围内的一段写成$u(t-a)-u(t-b)$。对于0和$\infty$的两个特殊情况,拉普拉斯变换不考虑小于0的情况,$u(0)$就是1,而$u(\infty)$就是0。

这里$(0,\pi)$就是对应$1-u(t-\pi)$,

(2)明显写出延时的变量代换

对应把时延中的各$u(t-a)$项,把f写成明显地含$t-a$的形式,这里要分别写成含$t$和$t-\pi$的形式

(3)按照「基本变换→频移→延时」的顺序进行变换

基本变换,$\sin 3t$的拉普拉斯变换是$\frac{3}{s^2+9}$,

另有频移$e^{2t}$,应把$s$写成$s-2$,

而延时$u(t-\pi)$对应的是$e^{-\pi s}$

(4)加起来

别忘了前面的符号和常数项!

反向变换

反向变换不需要化成分段的形式,保留阶跃函数u(t)即可

例题:

(1)忽略延时项

s域中延时项是指数函数的形式$e^{-as}$,这里$e^{-\pi s}$就是了。这里暂时忽略,后面再补上。

(2)凑频移项

分母有一次项的时候说明要凑平方,凑完了得到$s+a$,就是频移项。

这里需要凑平方,$s^2+2s+5=(s+1)^2+2^2$。频移项是$s+1$,把分子凑成$s+1$和2表示的形式。

(3)忽略频移项,作基本变换

把频移项$s+1$当作整体,

(4)补回频移项

注意之前还有个延时,这里先不写$t$,记为$t’$。

(5)补回延时项

$F’$和$F$相差一个$e^{-\pi s}$,对应要把$t’$换成$t-\pi$,并加上阶跃函数$u(t-\pi)$。

进阶II·时域导数和时域卷积

微分方程和Volterra积分方程的左边!

时域导数的公式非常简单

只有F前面是正号,其他的都是负号。

时域卷积的公式

注意卷积的定义是从0积到无穷,那么时域积分(0到无穷)实际上就是$1*f$,对应拉普拉斯变换

进阶III·s域导数和s域积分

专门考一道小计算

s域导数和时域类似,但有负号

s域积分的公式不好用(崔老师的答案里也不用),不必记忆。这里没有积分常数的问题,放心用导数就好。

使用时,要么F是某个基本变换的导数,要么F的导数是某个基本变换,反正方程就这一个,看着办吧(