线性规划饮食问题求解: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

相关推荐
cts61819 小时前
全文检索官网示例
python·全文检索·fastapi
半新半旧2 天前
FastAPI中间件
中间件·fastapi
爱吃羊的老虎3 天前
【后端】FastAPI的Pydantic 模型
数据库·后端·python·fastapi
Elastic 中国社区官方博客3 天前
使用 FastAPI 构建 Elasticsearch API
大数据·数据库·python·elasticsearch·搜索引擎·全文检索·fastapi
陈小桔4 天前
SQLALchemy
python·fastapi
alpszero4 天前
使用UV管理FastAPI项目
fastapi·uv
**梯度已爆炸**5 天前
Python Web框架详解:Flask、Streamlit、FastAPI
python·flask·fastapi·streamlit
斟的是酒中桃6 天前
基于Transformer的智能对话系统:FastAPI后端与Streamlit前端实现
前端·transformer·fastapi
蓝倾6 天前
淘宝获取商品分类接口操作指南
前端·后端·fastapi
蓝倾7 天前
1688平台根据关键词获取商品API接口操作指南
前端·后端·fastapi