Python package and CLI 1.0.19
PyRel 1.0.19 removes the currently unsupported std.re APIs and speeds up Graph.is_acyclic() on many large graphs.
How to install this version
Review the changes below before installing. To install this version, activate a compatible Python virtual environment and run:
python -m pip install "relationalai==1.0.19"For the latest installation instructions, see Start building with PyRel.
New Features and Enhancements
Section titled “New Features and Enhancements”Graph.is_acyclic()is now much faster on many large graphs, especially large trees and grids. In benchmarks for the change, the runtime for the largest tested grid graph dropped from about 751 seconds to 47 seconds.
Bug Fixes
Section titled “Bug Fixes”-
Removed unsupported
std.reAPIs. These APIs would raise errors if you tried to use them. All documentation pertaining tostd.rehas also been removed. -
Fixed active profile selection so that the
RAI_ACTIVE_PROFILEenvironment variable takes precedence overactive_profileinraiconfig.yaml. -
Fixed prescriptive models that combined filtered aggregates in one expression. Before, PyRel could leak one aggregate’s filter into another, so objectives and constraints could silently drop instead of being sent to the solver. For example:
from relationalai.semantics import Float, Integer, Modelfrom relationalai.semantics.reasoners.prescriptive import Problemmodel = Model("ScopedAggregates")X = model.Concept("X", identify_by={"i": Integer})X.v = model.Property(f"{X} has {Float:v}")model.define(X.new(i=1), X.new(i=2))problem = Problem(model, Float)problem.solve_for(X.v, name=["v", X.i], lower=0, upper=10)v = Float.ref()problem.satisfy(model.require(sum(v).where(X.v(v), X.i == 1) == 1.0))problem.satisfy(model.require(sum(v).where(X.v(v), X.i == 2) == 4.0))# The bug was triggered by combining two filtered aggregates in one expression.problem.minimize(sum(X.v).where(X.i == 1) + sum(X.v).where(X.i == 2))problem.satisfy(model.require(sum(X.v).where(X.i == 1) <= sum(X.v).where(X.i == 2)))problem.solve("highs")print(model.select(problem.num_min_objectives().alias("objective_count")).to_df())print(model.select(problem.num_constraints().alias("constraint_count")).to_df())print("objective_value:", problem.solve_info().objective_value)Output before the fix:
objective_count0 0constraint_count0 2objective_value: 0.0Output after the fix:
objective_count0 1constraint_count0 3objective_value: 5.0 -
Fixed
model.data()calls created on the same source line, such as inside a loop.model.data()creates a temporary data source each time you call it. Before, if two calls happened on the same line, PyRel could mistake them for the same source. That could make later queries fail even when the input data was valid.For example:
import pandas as pdfrom relationalai.semantics import Integer, Model, Stringm = Model("DataLoop")Item = m.Concept("Item", identify_by={"id": Integer, "name": String})batches = [pd.DataFrame([(1, "a"), (2, "b")], columns=["id", "name"]),pd.DataFrame([(3, "c"), (4, "d")], columns=["id", "name"]),]for batch in batches:m.define(Item.new(m.data(batch).to_schema()))print(m.select(Item.id, Item.name).to_df())Output before the fix:
RelQueryError: Query errorOutput after the fix:
id name0 1 a1 2 b2 3 c3 4 dNow PyRel gives each same-line data source its own deterministic sequence number, so queries compile and run correctly.