feat: 初始化Easy Patch插件及依赖文件

- 添加Blender插件核心文件:__init__.py、ui.py、property.py、preference.py
- 添加插件工具模块:g.py、loop.py、generate_loop.py、const.py、op.py
- 添加翻译工具:utils/trans.py
- 添加PuLP线性规划库及其依赖文件,包括CBC求解器二进制文件
- 添加.gitignore和VSCode配置文件
This commit is contained in:
2026-03-03 19:24:57 +08:00
commit ab91b120e6
44 changed files with 17551 additions and 0 deletions

View File

@@ -0,0 +1 @@
from .run_tests import pulpTestAll

View File

@@ -0,0 +1,51 @@
from pulp import *
import random
from itertools import product
def _bin_packing_instance(bins, seed=0):
packed_bins = [[] for _ in range(bins)]
bin_size = bins * 100
random.seed(seed)
for i in range(len(packed_bins)):
remaining_size = bin_size
while remaining_size >= 1:
item = random.randrange(1, remaining_size + 10)
packed_bins[i].append(item)
remaining_size -= item
packed_bins[i][-1] += remaining_size
all_items_with_bin = [(n, i) for i, l in enumerate(packed_bins) for n in l]
random.shuffle(all_items_with_bin)
items, packing = zip(*all_items_with_bin)
return items, packing, bin_size
def create_bin_packing_problem(bins, seed=0):
items, packing, bin_size = _bin_packing_instance(bins=bins, seed=seed)
prob = LpProblem("bin_packing", LpMinimize)
bin_indices = [i for i in range(len(items))]
item_indices = [i for i in range(len(items))]
using_bin = LpVariable.dicts("y", bin_indices, cat=LpBinary)
items_packed = LpVariable.dicts(
"x", indices=product(item_indices, bin_indices), cat=LpBinary
)
prob += lpSum(using_bin), "objective"
# pack every item
for i in item_indices:
prob += lpSum(items_packed[i, b] for b in bin_indices) == 1, f"pack_item_{i}"
# no bin overfilled
for b in bin_indices:
expr = (
lpSum([items[i] * items_packed[i, b] for i in item_indices])
<= bin_size * using_bin[b]
)
prob += expr, f"respect_bin_size_{b}"
return prob

View File

@@ -0,0 +1,33 @@
import unittest
import pulp
from pulp.tests import test_pulp, test_examples, test_gurobipy_env
def pulpTestAll(test_docs=False):
runner = unittest.TextTestRunner()
suite_all = get_test_suite(test_docs)
# we run all tests at the same time
ret = runner.run(suite_all)
if not ret.wasSuccessful():
raise pulp.PulpError("Tests Failed")
def get_test_suite(test_docs=False):
# Tests
loader = unittest.TestLoader()
suite_all = unittest.TestSuite()
# we get suite with all PuLP tests
pulp_solver_tests = loader.loadTestsFromModule(test_pulp)
suite_all.addTests(pulp_solver_tests)
# Add tests for gurobipy env
gurobipy_env = loader.loadTestsFromModule(test_gurobipy_env)
suite_all.addTests(gurobipy_env)
# We add examples and docs tests
if test_docs:
docs_examples = loader.loadTestsFromTestCase(test_examples.Examples_DocsTests)
suite_all.addTests(docs_examples)
return suite_all
if __name__ == "__main__":
pulpTestAll(test_docs=False)

View File

@@ -0,0 +1,36 @@
import os
import unittest
import pulp
import shutil
class Examples_DocsTests(unittest.TestCase):
def test_examples(self, examples_dir="../../examples"):
import importlib
this_file = os.path.realpath(__file__)
parent_dir = os.path.dirname(this_file)
files = os.listdir(os.path.join(parent_dir, examples_dir))
TMP_dir = "_tmp/"
if not os.path.exists(TMP_dir):
os.mkdir(TMP_dir)
for f_name in files:
if os.path.isdir(f_name):
continue
_f_name = "examples." + os.path.splitext(f_name)[0]
os.chdir(TMP_dir)
importlib.import_module(_f_name)
os.chdir("../")
shutil.rmtree(TMP_dir)
def test_doctest(self):
"""
runs all doctests
"""
import doctest
doctest.testmod(pulp)
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,122 @@
import unittest
from pulp import GUROBI, LpProblem, LpVariable, const
try:
import gurobipy as gp
from gurobipy import GRB
except ImportError:
gp = None
def check_dummy_env():
with gp.Env(params={"OutputFlag": 0}):
pass
def generate_lp() -> LpProblem:
prob = LpProblem("test", const.LpMaximize)
x = LpVariable("x", 0, 1)
y = LpVariable("y", 0, 1)
z = LpVariable("z", 0, 1)
prob += x + y + z, "obj"
prob += x + y + z <= 1, "c1"
return prob
class GurobiEnvTests(unittest.TestCase):
def setUp(self):
if gp is None:
self.skipTest("Skipping all tests in test_gurobipy_env.py")
self.options = {"Method": 0}
self.env_options = {"MemLimit": 1, "OutputFlag": 0}
def test_gp_env(self):
# Using gp.Env within a context manager
with gp.Env(params=self.env_options) as env:
prob = generate_lp()
solver = GUROBI(msg=False, env=env, **self.options)
prob.solve(solver)
solver.close()
check_dummy_env()
@unittest.SkipTest
def test_gp_env_no_close(self):
# Not closing results in an error for a single use license.
with gp.Env(params=self.env_options) as env:
prob = generate_lp()
solver = GUROBI(msg=False, env=env, **self.options)
prob.solve(solver)
self.assertRaises(gp.GurobiError, check_dummy_env)
def test_multiple_gp_env(self):
# Using the same env multiple times
with gp.Env(params=self.env_options) as env:
solver = GUROBI(msg=False, env=env)
prob = generate_lp()
prob.solve(solver)
solver.close()
solver2 = GUROBI(msg=False, env=env)
prob2 = generate_lp()
prob2.solve(solver2)
solver2.close()
check_dummy_env()
@unittest.SkipTest
def test_backward_compatibility(self):
"""
Backward compatibility check as previously the environment was not being
freed. On a single-use license this passes (fails to initialise a dummy
env).
"""
solver = GUROBI(msg=False, **self.options)
prob = generate_lp()
prob.solve(solver)
self.assertRaises(gp.GurobiError, check_dummy_env)
gp.disposeDefaultEnv()
solver.close()
def test_manage_env(self):
solver = GUROBI(msg=False, manageEnv=True, **self.options)
prob = generate_lp()
prob.solve(solver)
solver.close()
check_dummy_env()
def test_multiple_solves(self):
solver = GUROBI(msg=False, manageEnv=True, **self.options)
prob = generate_lp()
prob.solve(solver)
solver.close()
check_dummy_env()
solver2 = GUROBI(msg=False, manageEnv=True, **self.options)
prob.solve(solver2)
solver2.close()
check_dummy_env()
@unittest.SkipTest
def test_leak(self):
"""
Check that we cannot initialise environments after a memory leak. On a
single-use license this passes (fails to initialise a dummy env with a
memory leak).
"""
solver = GUROBI(msg=False, **self.options)
prob = generate_lp()
prob.solve(solver)
tmp = solver.model
solver.close()
solver2 = GUROBI(msg=False, **self.options)
prob2 = generate_lp()
prob2.solve(solver2)
self.assertRaises(gp.GurobiError, check_dummy_env)

File diff suppressed because it is too large Load Diff