サブロウ丸

Sabrou-mal サブロウ丸

主にプログラミングと数学

flopt v0.5.6:

flopt v.0.5.6を公開しました。

ソルバ開発の目線だとJacobian, Hessian用のAPIが一番使う機会が多いですかね? ユーザー目線だとprob.solve(optimized_variables=variable_list)が最も使えそうな気がします。

Update

1. problem type estimator API

問題の種類の推定機能はv0.5.5で導入したのですが、それを表示する関数です。 変数や、目的関数の種類、問題のタイプを表示します。気に入っています。

import flopt

x = flopt.Variable("x")
y = flopt.Variable("y")

prob = flopt.Problem()

prob += x + y
flopt.estimate_problem_type(prob)
>>> Problem
>>>    Name: None
>>>      Type         : Problem
>>>      sense        : Minimize
>>>      objective    : x+y
>>>      #constraints : 0
>>>      #variables   : 2 (Continuous 2)
>>> 
>>> Problem components
>>>    Variable: Continuous
>>>    Objective: Linear
>>>    Constraint: Non
>>> 
>>> Problem classes
>>>    --> lp
>>>    --> mip
>>>        ising
>>>    --> quadratic
>>>        permutation
>>>        blackbox
>>>        blackbox with interger variables
>>>    --> nonlinear
>>>    --> nonlinear with integer variables

2. Jacobian, Hessian API

Jacobian, Hessianの計算。Polynominal.pyをいじって作っています。 ただ、今のところ多項式にしかこれらのAPIを使えないので、三角関数などを含む場合は使えません。 次の項目のMath operationに微分を定義してやればいけるかな? まぁExpression(式)を計算グラフ的に定義しているので、自動微分を使えるようにしたいですね。

x = flopt.Variable.array("x", 3)
f = flopt.Prod(x)  # f is x0 * x1 * x2

# jacobian expression
jac = f.jac(x)
print("jac[0] =", jac[0].getName())  # 0-index value of \nabla f
print("jac[1] =", jac[1].getName())  # 1-index value of \nabla f
print("jac[2] =", jac[2].getName())  # 2-index value of \nabla f

# jacobian value
print("jac =", jac.value())

# hessian expression
hess = f.hess(x)
print(hess)

# hessian value
print(hess.value())

3. Math operation API

  • sqrt, exp, cos, sin, tan, log, abs, floor, ceil
  • dot, sum, prod, value
flopt.sin(flopt.sqrt(flopt.abs(x1 - x2 - 47)))

4. optimized_variables API

最適化対象の変数の固定用。交互方向乗数法 (Alternating. Direction Method of Multipliers: ADMM)などへの利用を意識して追加しました。またドキュメントでは座標降下法のレシピを追加しています。

# Coordinate Descent
for _ in range(10):
    # optimize only x_0
    prob.solve(optimized_variables=[x[0]])
    # optimize only x_1
    prob.solve(optimized_variables=[x[1]])

実装はclass Problem::solve()内を見れば一目瞭然。

        if optimized_variables is None:
            solution = Solution(self.getVariables())
        else:
            assert (
                set(optimized_variables) <= self.getVariables()
            ), "optimized_variables containes variables that are not in the problem"
            solution = Solution(optimized_variables)

5. Replace variables API of Problem

問題に含まれる変数を別のものに入れ替える関数。 モチベーションとしては線形計画問題の標準形への変換時に必要だったので追加しました。 この形のAPIにすると、結構一般化されていて、他の使い道もありそうな感じがします。

import flopt

x = flopt.Variable("x")

prob = flopt.Problem()
prob += x
print(prob)
>>> Name: None
>>>   Type         : Problem
>>>   sense        : Minimize
>>>   objective    : x+0
>>>   #constraints : 0
>>>   #variables   : 1 (Continuous 1)

x_p = flopt.Variable("x_plus", lowBound=0)
x_m = flopt.Variable("x_minus", lowBound=0)
prob = prob.replace({x: x_p - x_m})
print(prob)
>>> Name: None
>>>   Type         : Problem
>>>   sense        : Minimize
>>>   objective    : x_plus-x_minus
>>>   #constraints : 0
>>>   #variables   : 2 (Continuous 2)

6. Clone API of Problem

問題の複製。今までなかったのね。注意なのが.clone()だと問題は複製されますが、内部で使用される変数オブジェクトはそのまま使用されます。なので使い方によっては意図しない値の設定により、バグが発生してしまうかも。

cloned_prob = prob.clone()

変数まで複製したい場合は、variable_cloneを渡してやります。

cloned_prob = prob.clone(variable_clone=True)

API Change

1. CustomExpression arguments (arg → args)

リスト渡しが基本になるかなと思ったので複数形にしました。こういう変更はあまりしない方が良いんでしょうけどね。

def f(x, y):
    return x + y

x = flopt.Variable("x")
y = flopt.Variable("y")

custom_expression = flopt.CustomeExpression(f, args=[x, y])