线性规划饮食问题求解:FastAPI作为服务端+libhv作为客户端实现

之前在 Pyomo介绍-CSDN博客 中介绍过通过Pyomo求解线性规划问题,这里使用FastAPI作为服务端,开源网络库libhv作为客户端,求解饮食成本最小化问题。

服务端测试代码test_fastapi_pyomo_server.py如下:

python 复制代码
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from pyomo.environ import *
import math
import json

def parse_json(data):
	model = ConcreteModel()

	model.F = Set(initialize=data['sets']['F'])
	model.N = Set(initialize=data['sets']['N'])

	model.c = Param(model.F, initialize=data['params']['c'], within=PositiveReals)
	def parse_a(model, food, nutr):
		return data['params']['a'][food][nutr]
	model.a = Param(model.F, model.N, initialize=parse_a, within=NonNegativeReals)
	model.V = Param(model.F, initialize=data['params']['V'], within=PositiveReals)
	model.Nmin = Param(model.N, initialize=data['params']['Nmin'], within=NonNegativeReals, default=0.0)
	def parse_Nmax(model, nutr):
		val = data['params']['Nmax'][nutr]
		return val if val != "inf" else math.inf
	model.Nmax = Param(model.N, initialize=parse_Nmax, within=NonNegativeReals)
	model.Vmax = Param(initialize=data['params']['Vmax'], within=PositiveReals)

	return model

def linear_programming(data):
	model = parse_json(data)

	model.x = Var(model.F, within=NonNegativeIntegers)
	model.y = Var(model.F, within=Binary)

	model.cost = Objective(expr=sum(model.c[i]*model.x[i] for i in model.F), sense=minimize)

	def nutrient_rule(model, j):
		value = sum(model.a[i,j]*model.x[i] for i in model.F)
		return inequality(model.Nmin[j], value, model.Nmax[j])
	model.nutrient_limit = Constraint(model.N, rule=nutrient_rule)

	def volume_rule(model):
		return sum(model.V[i]*model.x[i] for i in model.F) <= model.Vmax
	model.volume = Constraint(rule=volume_rule)

	def select_rule(model):
		return sum(model.y[i] for i in model.F) == data['number']
	model.select = Constraint(rule=select_rule)

	def linking_upper_rule(model, f):
		return model.x[f] <= model.y[f] * 1e6
	model.linking_upper = Constraint(model.F, rule=linking_upper_rule)

	def linking_lower_rule(model, f):
		return model.x[f] >= model.y[f]
	model.linking_lower = Constraint(model.F, rule=linking_lower_rule)

	solver = SolverFactory('glpk')
	ret = solver.solve(model)
	if ret.solver.termination_condition != TerminationCondition.optimal:
		return JSONResponse(status_code=400, content={"error": "no optimal solution"})

	results = {"selected_food": [], "nutrients": []}
	results["cost"] = f"{value(model.cost):.2f}"

	count = 0
	for f in model.F:
		v = int(value(model.x[f]))
		if v != 0:
			results["selected_food"].append({f:v})
			count += 1

	if count != data['number']:
		return JSONResponse(status_code=400, content={"error": "unmatched number", "count": count, "number": data['number']})

	def inf_convert(val):
		if val == math.inf:
			return "INF"
		elif val == -math.inf:
			return "-INF"
		return val

	for n in model.N:
		actual = sum(value(model.a[f,n] * model.x[f]) for f in model.F)
		results["nutrients"].append({
			n:f"{actual:.4f}",
			"boundary":[inf_convert(value(model.Nmin[n])), inf_convert(value(model.Nmax[n]))]
		})

	return JSONResponse(status_code=200, content=results)

app = FastAPI()

@app.post("/api/optimize")
async def optimize_diet(request: Request):
	json_bytes = await request.body()

	json_str = json_bytes.decode('utf-8')
	data = json.loads(json_str)
	if not isinstance(data, dict):
		return JSONResponse(status_code=400, content={"error": "Invalid JSON format"})

	return linear_programming(data)

执行以下命令启动服务:

bash 复制代码
fastapi run test_fastapi_pyomo_server.py

执行结果如下图所示:

客户端测试代码如下所示:

cpp 复制代码
int test_libhv_http_client_diet()
{
	constexpr char file_name[]{ "../../../testdata/diet.json" };
	std::ifstream in_file(file_name);
	if (!in_file.is_open()) {
		std::cerr << "Error: failed to open json file: " << file_name << std::endl;
		return -1;
	}

	auto j = hv::Json::parse(in_file);
	constexpr int number{ 5 };
	j["number"] = number;

	const std::string server_url{ "http://192.168.1.28:8000" };
	HttpRequest request{};
	request.method = HTTP_POST;
	request.url = server_url + "/api/optimize";
	request.body = j.dump();
	request.headers["Content-Type"] = "application/json";
	request.timeout = 2;

	hv::HttpClient client{};
	HttpResponse response{};
	if (auto ret = client.send(&request, &response); ret == 0) {
		if (response.status_code == HTTP_STATUS_OK) {
			hv::Json j = hv::Json::parse(response.body);
			std::cout << "result: " << j.dump() << std::endl;

			constexpr char result_name[]{ "../../../testdata/result.json" };
			std::ofstream out_file(result_name);
			if (!out_file.is_open()) {
				std::cerr << "Error: faild to open file: " << result_name << std::endl;
				return -1;
			}
			out_file << j.dump(2);
		} else {
			std::cerr << "status code: " << response.status_code << ", status message: " << response.status_message() << ", body: " << response.body << std::endl;
			return -1;
		}
	} else {
		std::cerr << "Error: failed to send, error code: " << ret << std::endl;
		return -1;
	}

	return 0;
}

执行结果如下图所示:与之前结果一致

与服务端在同一局域网内的任何机子都可以作为客户端。

diet.json内容如下:也可以完全通过nlohmann/json创建

bash 复制代码
{
  "sets": {
    "F": [
      "Cheeseburger",
      "Ham Sandwich",
      "Hamburger",
      "Fish Sandwich",
      "Chicken Sandwich",
      "Fries",
      "Sausage Biscuit",
      "Lowfat Milk",
      "Orange Juice"
    ],
    "N": [
      "Cal",
      "Carbo",
      "Protein",
      "VitA",
      "VitC",
      "Calc",
      "Iron"
    ]
  },
  "params": {
    "c": {
      "Cheeseburger": 1.84,
      "Ham Sandwich": 2.19,
      "Hamburger": 1.84,
      "Fish Sandwich": 1.44,
      "Chicken Sandwich": 2.29,
      "Fries": 0.77,
      "Sausage Biscuit": 1.29,
      "Lowfat Milk": 0.6,
      "Orange Juice": 0.72
    },
    "V": {
      "Cheeseburger": 4.0,
      "Ham Sandwich": 7.5,
      "Hamburger": 3.5,
      "Fish Sandwich": 5.0,
      "Chicken Sandwich": 7.3,
      "Fries": 2.6,
      "Sausage Biscuit": 4.1,
      "Lowfat Milk": 8.0,
      "Orange Juice": 12.0
    },
    "Vmax": 75.0,
    "Nmin": {
      "Cal": 2000.0,
      "Carbo": 350.0,
      "Protein": 55.0,
      "VitA": 100.0,
      "VitC": 100.0,
      "Calc": 100.0,
      "Iron": 100.0
    },
    "Nmax": {
      "Cal": "inf",
      "Carbo": 375.0,
      "Protein": "inf",
      "VitA": "inf",
      "VitC": "inf",
      "Calc": "inf",
      "Iron": "inf"
    },
    "a": {
      "Cheeseburger": {
        "Cal": 510.0,
        "Carbo": 34.0,
        "Protein": 28.0,
        "VitA": 15.0,
        "VitC": 6.0,
        "Calc": 30.0,
        "Iron": 20.0
      },
      "Ham Sandwich": {
        "Cal": 370.0,
        "Carbo": 35.0,
        "Protein": 24.0,
        "VitA": 15.0,
        "VitC": 10.0,
        "Calc": 20.0,
        "Iron": 20.0
      },
      "Hamburger": {
        "Cal": 500.0,
        "Carbo": 42.0,
        "Protein": 25.0,
        "VitA": 6.0,
        "VitC": 2.0,
        "Calc": 25.0,
        "Iron": 20.0
      },
      "Fish Sandwich": {
        "Cal": 370.0,
        "Carbo": 38.0,
        "Protein": 14.0,
        "VitA": 2.0,
        "VitC": 0.0,
        "Calc": 15.0,
        "Iron": 10.0
      },
      "Chicken Sandwich": {
        "Cal": 400.0,
        "Carbo": 42.0,
        "Protein": 31.0,
        "VitA": 8.0,
        "VitC": 15.0,
        "Calc": 15.0,
        "Iron": 8.0
      },
      "Fries": {
        "Cal": 220.0,
        "Carbo": 26.0,
        "Protein": 3.0,
        "VitA": 0.0,
        "VitC": 15.0,
        "Calc": 0.0,
        "Iron": 2.0
      },
      "Sausage Biscuit": {
        "Cal": 345.0,
        "Carbo": 27.0,
        "Protein": 15.0,
        "VitA": 4.0,
        "VitC": 0.0,
        "Calc": 20.0,
        "Iron": 15.0
      },
      "Lowfat Milk": {
        "Cal": 110.0,
        "Carbo": 12.0,
        "Protein": 9.0,
        "VitA": 10.0,
        "VitC": 4.0,
        "Calc": 30.0,
        "Iron": 0.0
      },
      "Orange Juice": {
        "Cal": 80.0,
        "Carbo": 20.0,
        "Protein": 1.0,
        "VitA": 2.0,
        "VitC": 120.0,
        "Calc": 2.0,
        "Iron": 2.0
      }
    }
  }
}

GitHub

服务端:https://github.com/fengbingchun/Python_Test

客户端:https://github.com/fengbingchun/OpenSSL_Test

相关推荐
小宁爱Python10 小时前
FastAPI+Sqlite+HTML的登录注册与文件上传系统:完整实现指南
sqlite·html·fastapi
fairymt21 小时前
LoRA 问答微调与部署全流程:基于 LLaMA-Factory + DeepSeek + FastAPI 打造专属大模型
fastapi
chenkangck5010 天前
Uvicorn 入门详解
python·fastapi
remandancy.h14 天前
FastAPI:(2)开启FastAPI
fastapi
里探14 天前
FastAPI的初步学习(Django用户过来的)
python·django·fastapi
从零开始学习人工智能14 天前
基于FastAPI与Selenium的智能开关状态管理系统实践
selenium·adb·fastapi
Python私教16 天前
FastAPI本地文档的定制技巧
fastapi
沛沛老爹17 天前
Celery+fastAPI/Flask实现高性能应用
python·flask·fastapi·celery·web框架集成
remandancy.h17 天前
FastAPI:(10)中间件与CORS
中间件·fastapi
浠寒AI19 天前
【FastAPI高级实战】结合查询参数与SQLModel Joins实现高效多表查询(分页、过滤、计数)
fastapi