1. 简介:
向量化建模是一种高效的数学建模和编程技术,它涉及到对向量、矩阵和更高维数组进行操作,以实现操作的同时性和批量处理。在优化和数据分析等领域,向量化建模可以极大地提高计算效率,特别是当涉及到大量的重复计算时。由于向量化建模具有表述优势、操作优势、计算性能、可扩展性等优势,使得其适合于解决很大一类实际问题
MindOpt APL (MindOpt Algebraic Programming Language, MAPL) 是一种高效且通用的代数建模语言,主要用于数学规划问题的建模,并支持调用多种求解器求解。它当前支持通用的线性、非线性、混合整数问题的建模。其语法贴近数学语言,与代数数学公式很接近,易学易写易读易维护。且MindOpt APL支持对接20+种优化求解器,可用一行命令就切换,大大提升了用户在优化问题求解环节的方案丰富度,降低风险和使用门槛。
那么MAPL是如何支持向量化建模的呢,让我们一起来看下文
2. MAPL中的向量化建模:
MAPL通过对向量/矩阵常量参数与变量的声明以及运用张量常量表达式的计算、向量/矩阵变量表达式的计算对问题进行建模
本篇我们介绍述向量/矩阵常量参数的声明以及运用张量常量表达式的计算的语法
其中常量参数的声明有三种方式
- 显式定义常量参数
声明一个m x n 的矩阵式常量:
param matrix_name = [A_11, ... , A_1n (n个实数) ;
... (m行)
A_n1, ... , A_mn (n个实数) ];
声明一个 1 x n 向量式常量:
param vector_name = [a_11, ... , a_1n (n个实数) ];
- 使用MAPL的内置函数生成常量参数
param v = 内置函数名([param_list]);
- 从表格式csv文件中读取矩阵形式的常量参数
param v = read_csv([file_name,] [param_list]);
张量常量表达式的数学运算符列表如下:
类型 |
操作符 |
说明 |
是否支持标量 |
用例 |
一元操作符 |
|
向量/矩阵加法 |
是 |
|
|
向量/矩阵减法,或者参数求反 |
是 |
|
|
|
逐元素乘法 |
否 |
|
|
|
逐元素除法 |
否 |
|
|
|
向量/矩阵乘法 |
是 |
|
|
|
矩阵转置 |
否 |
|
|
|
向量/矩阵逐元素除以某标量 |
是 |
|
|
二元操作符 |
|
逐元素的p次幂 |
是 |
|
|
逐元素的p次幂 |
是 |
|
|
索引操作符 |
|
获取指定位置的值 |
否 |
|
其中“支持标量”的操作符,不仅可以支持标量间或者张量间的常规计算,也能以某种方式实现标量与张量之间的混合计算,浏览文档查看
3. 语法示例:
- 显式定义常量参数:通过列出所有元素的值来创建矩阵或向量。例如,声明一个
m×n
矩阵的语法是param matrix_name = [A_11, ..., A_1n; ...; A_m1, ..., A_mn];
,声明一个1×m
向量的语法是param vector_name = [a_1, ..., a_n];
。
- 获取参数属性:可以使用
.row
和.col
来获取矩阵/向量参数的行数和列数,例如获取矩阵X
的大小。
param vec = [1,3,5,7,9,11]; print "Shape of vec: ({},{})." % vec.row, vec.col; param A =[1,2,3; 4,5,6; 7,8,9]; print "Shape of A: ({},{})." % A.row, A.col;
Shape of vec: (1,6). Shape of A: (3,3).
- 参数的索引:可通过行/列号对向量/矩阵中的元素进行索引,索引从0开始。例如,对于向量
vec
,vec[4]
或vec[0,4]
都指向向量的第五个元素;对于矩阵A
,A[i-1,j-1]
指向矩阵的第i
行第j
列元素。
param A =[1,2,3; 4,5,6; 7,8,9]; #遍历输出所有大于等于5的项 for (i,j) in (1..A.row) * (1..A.col): if A[i-1,j-1] >= 5 then print "A[{},{}]={}"%i,j,A[i-1, j-1];
A[2,2]=5 A[2,3]=6 A[3,1]=7 A[3,2]=8 A[3,3]=9
- 使用内置函数生成常量参数:MAPL 提供了一些内置函数如
ones, zeros, identity, random
和normal
来快速生成特定类型的向量/矩阵常量。例如,ones(3,7)
创建一个3x7的全1矩阵,random(3,7)
创建一个满足[0,1]均匀分布的3x7随机矩阵。
clear model; print "ones matrix: ones(3,7)"; param one = ones(3,7); print one; print "--------------"; print "zeros matrix: zeros(3,7)"; param zero = zeros(3,7); print zero; print "--------------"; print "identity matrix: identity(4,4)"; param E = identity(4,4); print E; print "--------------"; print "random matrix: random(shape=(3,7), seed=12345)"; param X = random(shape=(3,7), seed=123456); print X; print "--------------"; print "normal matrix, (mu,sigma)=(-10,10): normal(shape=(3,7), mu=-10, sigma=10, seed=12345);"; param Y = normal(shape=(3,7), mu=-10, sigma=10, seed=123456); print Y;
ones matrix: ones(3,7) [[1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1]] -------------- zeros matrix: zeros(3,7) [[0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0]] -------------- identity matrix: identity(4,4) [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]] -------------- random matrix: random(shape=(3,7), seed=12345) [[0.51491, 0.89813, 0.70582, 0.77883, 0.93162, 0.62877, 0.33987], [0.21579, 0.11833, 0.08322, 0.38544, 0.96056, 0.94246, 0.35274], [0.17555, 0.15881, 0.95409, 0.14046, 0.03238, 0.84024, 0.81065]] -------------- normal matrix, (mu,sigma)=(-10,10): normal(shape=(3,7), mu=-10, sigma=10, seed=12345); [[ -0.47484, -9.64320, -0.25816, -2.80899, -8.15235, -3.80674, -21.38680], [-16.41576, -5.56788, -11.10240, -11.66778, -4.98887, -13.55322, -13.37890], [ -4.19033, -0.16199, -9.42198, -2.38052, -17.12964, -14.43160, -19.74602]]
- 从CSV文件中读取矩阵形式的常量参数:使用 read_csv 函数可以从CSV文件中读取数据并自动将其视为一个常量矩阵或向量。例如,
param X := read_csv("iris.data", use_col="0,1,2,3", skip =1);
会从iris.data
文件中读取前四列数据作为矩阵X。然后可以查询矩阵的形状信息,并通过索引访问其中的元素。
X,X,X,X,Y 5.1,3.5,1.4,0.2,1 4.9,3.0,1.4,0.2,1 4.7,3.2,1.3,0.2,1 4.6,3.1,1.5,0.2,1 5.0,3.6,1.4,0.2,1 5.7,3.0,4.2,1.2,-1 5.7,2.9,4.2,1.3,-1 6.2,2.9,4.3,1.3,-1 5.1,2.5,3.0,1.1,-1 5.7,2.8,4.1,1.3,-1
param X:= read_csv("iris.data", use_col="0,1,2,3", skip =1); param y:= read_csv("iris.data", use_col=4,skip =1); print "Shape of X is: ({},{})." % X.row, X.col; print "Shape of y is: ({},{})." % y.row, y.col; print "X[0,0] is {:.1f}" % X[0, 0]; print "X[9,2]*y[5] is {:.1f}" % X[9, 2] * y[5];
Shape of X is: (10,4). Shape of y is: (10,1). X[0,0] is 5.1 X[9,2]*y[5] is -4.1
表达式的计算示例(向量及矩阵乘法)更多示例:
param a = [1,2,3,4,5,6,7,8,9,10]; param b = [10,9,8,7,6,5,4,3,2,1]; print "a * b' is: ", a * b'; print "b'*a is:"; print b'*a;
a * b' is: 220 b'*a is: [[ 10, 20, 30, 40, 50, 60, 70, 80, 90, 100], [ 9, 18, 27, 36, 45, 54, 63, 72, 81, 90], [ 8, 16, 24, 32, 40, 48, 56, 64, 72, 80], [ 7, 14, 21, 28, 35, 42, 49, 56, 63, 70], [ 6, 12, 18, 24, 30, 36, 42, 48, 54, 60], [ 5, 10, 15, 20, 25, 30, 35, 40, 45, 50], [ 4, 8, 12, 16, 20, 24, 28, 32, 36, 40], [ 3, 6, 9, 12, 15, 18, 21, 24, 27, 30], [ 2, 4, 6, 8, 10, 12, 14, 16, 18, 20], [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]
4. 一个案例代码示例:
线性支持向量机(Linear Support Vector Machine, SVM)是一种广泛应用于模式识别、分类以及回归问题的监督学习算法。SVM的核心思想是寻找一个最优的超平面,用以划分不同类别的数据点,同时最大化各类数据点到该超平面的最小间隔(margin)。
代码如下:
# 1.读取iris数据 param X:= read_csv("$data_path/iris.data", use_col="0,1,2,3"); param y:= read_csv("$data_path/iris.data", use_col=4); param m = X.row; param n = X.col; # 2.LinearSVM问题建模 param rho = 0.2; var w(n) >= -1 <= 1; # Bounded Model Parameter var eps(m) >= 0; minimize w' * w + rho * sum(eps); s.t. eps >= 1 - X*w.*y; # 3.调用开源求解器Ipopt求解 option solver ipopt; solve; # 4.验证并分析结果 param total_loss = rho * ones(1, m) * eps + w'*w; param prec = 1/m * sum{i in 0..m-1} if (sum{j in 0..n-1} (w[j]*X[i, j])) * y[i] > 0 then 1 else 0 end; print "(Objective, Precision): ({:.2f}, {:.1f})" % total_loss, prec;
结果查看可浏览文档,介绍更全面!